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 }