github.com/onflow/flow-go@v0.33.17/consensus/hotstuff/signature/randombeacon_signer_store_test.go (about)

     1  package signature
     2  
     3  import (
     4  	"errors"
     5  	"math/rand"
     6  	"testing"
     7  
     8  	"github.com/stretchr/testify/assert"
     9  	"github.com/stretchr/testify/require"
    10  	"github.com/stretchr/testify/suite"
    11  
    12  	"github.com/onflow/flow-go/consensus/hotstuff/model"
    13  	"github.com/onflow/flow-go/crypto"
    14  	"github.com/onflow/flow-go/module"
    15  	mockmodule "github.com/onflow/flow-go/module/mock"
    16  	"github.com/onflow/flow-go/storage"
    17  	mockstorage "github.com/onflow/flow-go/storage/mock"
    18  	"github.com/onflow/flow-go/utils/unittest"
    19  )
    20  
    21  type BeaconKeyStore struct {
    22  	suite.Suite
    23  	epochLookup *mockmodule.EpochLookup
    24  	beaconKeys  *mockstorage.SafeBeaconKeys
    25  	store       *EpochAwareRandomBeaconKeyStore
    26  }
    27  
    28  func TestBeaconKeyStore(t *testing.T) {
    29  	suite.Run(t, new(BeaconKeyStore))
    30  }
    31  
    32  func (suite *BeaconKeyStore) SetupTest() {
    33  	suite.epochLookup = mockmodule.NewEpochLookup(suite.T())
    34  	suite.beaconKeys = mockstorage.NewSafeBeaconKeys(suite.T())
    35  	suite.store = NewEpochAwareRandomBeaconKeyStore(suite.epochLookup, suite.beaconKeys)
    36  }
    37  
    38  // TestHappyPath tests the happy path, where the epoch is known and there is a safe beacon key available.
    39  func (suite *BeaconKeyStore) TestHappyPath() {
    40  	view := rand.Uint64()
    41  	epoch := rand.Uint64()
    42  	expectedKey := unittest.KeyFixture(crypto.BLSBLS12381)
    43  	suite.epochLookup.On("EpochForViewWithFallback", view).Return(epoch, nil)
    44  	suite.beaconKeys.On("RetrieveMyBeaconPrivateKey", epoch).Return(expectedKey, true, nil)
    45  
    46  	key, err := suite.store.ByView(view)
    47  	require.NoError(suite.T(), err)
    48  	assert.Equal(suite.T(), expectedKey, key)
    49  }
    50  
    51  // Test_EpochLookup_UnknownEpochError tests that when EpochLookup returns
    52  // model.ErrViewForUnknownEpoch it is propagated to the caller of ByView.
    53  func (suite *BeaconKeyStore) Test_EpochLookup_ViewForUnknownEpoch() {
    54  	view := rand.Uint64()
    55  	suite.epochLookup.On("EpochForViewWithFallback", view).Return(uint64(0), model.ErrViewForUnknownEpoch)
    56  
    57  	key, err := suite.store.ByView(view)
    58  	require.ErrorIs(suite.T(), err, model.ErrViewForUnknownEpoch)
    59  	assert.Nil(suite.T(), key)
    60  }
    61  
    62  // Test_EpochLookup_UnexpectedError tests that an exception from EpochLookup is
    63  // propagated to the caller of ByView.
    64  func (suite *BeaconKeyStore) Test_EpochLookup_UnexpectedError() {
    65  	view := rand.Uint64()
    66  	exception := errors.New("unexpected error")
    67  	suite.epochLookup.On("EpochForViewWithFallback", view).Return(uint64(0), exception)
    68  
    69  	key, err := suite.store.ByView(view)
    70  	require.ErrorIs(suite.T(), err, exception)
    71  	assert.Nil(suite.T(), key)
    72  }
    73  
    74  // Test_BeaconKeys_Unsafe tests that if SafeBeaconKeys reports an unsafe key,
    75  // ByView returns that no beacon key is available.
    76  func (suite *BeaconKeyStore) Test_BeaconKeys_Unsafe() {
    77  	view := rand.Uint64()
    78  	epoch := rand.Uint64()
    79  	suite.epochLookup.On("EpochForViewWithFallback", view).Return(epoch, nil)
    80  	suite.beaconKeys.On("RetrieveMyBeaconPrivateKey", epoch).Return(nil, false, nil)
    81  
    82  	key, err := suite.store.ByView(view)
    83  	require.ErrorIs(suite.T(), err, module.ErrNoBeaconKeyForEpoch)
    84  	assert.Nil(suite.T(), key)
    85  }
    86  
    87  // Test_BeaconKeys_NotFound tests that if SafeBeaconKeys returns storage.ErrNotFound,
    88  // ByView returns that no beacon key is available.
    89  func (suite *BeaconKeyStore) Test_BeaconKeys_NotFound() {
    90  	view := rand.Uint64()
    91  	epoch := rand.Uint64()
    92  	suite.epochLookup.On("EpochForViewWithFallback", view).Return(epoch, nil)
    93  	suite.beaconKeys.On("RetrieveMyBeaconPrivateKey", epoch).Return(nil, false, storage.ErrNotFound)
    94  
    95  	key, err := suite.store.ByView(view)
    96  	require.ErrorIs(suite.T(), err, module.ErrNoBeaconKeyForEpoch)
    97  	assert.Nil(suite.T(), key)
    98  }
    99  
   100  // Test_BeaconKeys_NotFound tests that if SafeBeaconKeys returns storage.ErrNotFound,
   101  // ByView initially returns that no beacon key is available. But if SafeBeaconKeys
   102  // later returns a safe key, ByView should thereafter return the beacon key.
   103  // In other words, NotFound results should not be cached.
   104  func (suite *BeaconKeyStore) Test_BeaconKeys_NotFoundThenAvailable() {
   105  	view := rand.Uint64()
   106  	epoch := rand.Uint64()
   107  	suite.epochLookup.On("EpochForViewWithFallback", view).Return(epoch, nil)
   108  
   109  	var retKey crypto.PrivateKey
   110  	var retSafe bool
   111  	var retErr error
   112  	suite.beaconKeys.On("RetrieveMyBeaconPrivateKey", epoch).
   113  		Return(
   114  			func(_ uint64) crypto.PrivateKey { return retKey },
   115  			func(_ uint64) bool { return retSafe },
   116  			func(_ uint64) error { return retErr },
   117  		)
   118  
   119  	// 1 - return storage.ErrNotFound
   120  	retKey = nil
   121  	retSafe = false
   122  	retErr = storage.ErrNotFound
   123  
   124  	key, err := suite.store.ByView(view)
   125  	require.ErrorIs(suite.T(), err, module.ErrNoBeaconKeyForEpoch)
   126  	assert.Nil(suite.T(), key)
   127  
   128  	// 2 - return a safe beacon key
   129  	retKey = unittest.RandomBeaconPriv().PrivateKey
   130  	retSafe = true
   131  	retErr = nil
   132  
   133  	key, err = suite.store.ByView(view)
   134  	require.NoError(suite.T(), err)
   135  	assert.Equal(suite.T(), retKey, key)
   136  }