github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/state/protocol/validity_test.go (about) 1 package protocol_test 2 3 import ( 4 "testing" 5 6 "github.com/onflow/crypto" 7 "github.com/stretchr/testify/require" 8 9 "github.com/onflow/flow-go/model/flow" 10 "github.com/onflow/flow-go/model/flow/filter" 11 "github.com/onflow/flow-go/state/protocol" 12 "github.com/onflow/flow-go/utils/unittest" 13 ) 14 15 var participants = unittest.IdentityListFixture(20, unittest.WithAllRoles()) 16 17 func TestEpochSetupValidity(t *testing.T) { 18 t.Run("invalid first/final view", func(t *testing.T) { 19 _, result, _ := unittest.BootstrapFixture(participants) 20 setup := result.ServiceEvents[0].Event.(*flow.EpochSetup) 21 // set an invalid final view for the first epoch 22 setup.FinalView = setup.FirstView 23 24 err := protocol.IsValidEpochSetup(setup, true) 25 require.Error(t, err) 26 }) 27 28 t.Run("non-canonically ordered identities", func(t *testing.T) { 29 _, result, _ := unittest.BootstrapFixture(participants) 30 setup := result.ServiceEvents[0].Event.(*flow.EpochSetup) 31 // randomly shuffle the identities so they are not canonically ordered 32 var err error 33 setup.Participants, err = setup.Participants.Shuffle() 34 require.NoError(t, err) 35 err = protocol.IsValidEpochSetup(setup, true) 36 require.Error(t, err) 37 }) 38 39 t.Run("invalid cluster assignments", func(t *testing.T) { 40 _, result, _ := unittest.BootstrapFixture(participants) 41 setup := result.ServiceEvents[0].Event.(*flow.EpochSetup) 42 // create an invalid cluster assignment (node appears in multiple clusters) 43 collector := participants.Filter(filter.HasRole[flow.Identity](flow.RoleCollection))[0] 44 setup.Assignments = append(setup.Assignments, []flow.Identifier{collector.NodeID}) 45 46 err := protocol.IsValidEpochSetup(setup, true) 47 require.Error(t, err) 48 }) 49 50 t.Run("short seed", func(t *testing.T) { 51 _, result, _ := unittest.BootstrapFixture(participants) 52 setup := result.ServiceEvents[0].Event.(*flow.EpochSetup) 53 setup.RandomSource = unittest.SeedFixture(crypto.KeyGenSeedMinLen - 1) 54 55 err := protocol.IsValidEpochSetup(setup, true) 56 require.Error(t, err) 57 }) 58 59 t.Run("node role missing", func(t *testing.T) { 60 _, result, _ := unittest.BootstrapFixture(participants) 61 setup := result.ServiceEvents[0].Event.(*flow.EpochSetup) 62 allWithoutExecutionNodes := setup.Participants.Filter(func(identitySkeleton *flow.IdentitySkeleton) bool { 63 return identitySkeleton.Role != flow.RoleExecution 64 }) 65 setup.Participants = allWithoutExecutionNodes 66 67 err := protocol.IsValidEpochSetup(setup, true) 68 require.Error(t, err) 69 }) 70 71 t.Run("network addresses are not unique", func(t *testing.T) { 72 _, result, _ := unittest.BootstrapFixture(participants) 73 setup := result.ServiceEvents[0].Event.(*flow.EpochSetup) 74 setup.Participants[0].Address = setup.Participants[1].Address 75 76 err := protocol.IsValidEpochSetup(setup, true) 77 require.Error(t, err) 78 }) 79 80 t.Run("no cluster assignment", func(t *testing.T) { 81 _, result, _ := unittest.BootstrapFixture(participants) 82 setup := result.ServiceEvents[0].Event.(*flow.EpochSetup) 83 setup.Assignments = flow.AssignmentList{} 84 85 err := protocol.IsValidEpochSetup(setup, true) 86 require.Error(t, err) 87 }) 88 } 89 90 func TestBootstrapInvalidEpochCommit(t *testing.T) { 91 t.Run("inconsistent counter", func(t *testing.T) { 92 _, result, _ := unittest.BootstrapFixture(participants) 93 setup := result.ServiceEvents[0].Event.(*flow.EpochSetup) 94 commit := result.ServiceEvents[1].Event.(*flow.EpochCommit) 95 // use a different counter for the commit 96 commit.Counter = setup.Counter + 1 97 98 err := protocol.IsValidEpochCommit(commit, setup) 99 require.Error(t, err) 100 }) 101 102 t.Run("inconsistent cluster QCs", func(t *testing.T) { 103 _, result, _ := unittest.BootstrapFixture(participants) 104 setup := result.ServiceEvents[0].Event.(*flow.EpochSetup) 105 commit := result.ServiceEvents[1].Event.(*flow.EpochCommit) 106 // add an extra QC to commit 107 extraQC := unittest.QuorumCertificateWithSignerIDsFixture() 108 commit.ClusterQCs = append(commit.ClusterQCs, flow.ClusterQCVoteDataFromQC(extraQC)) 109 110 err := protocol.IsValidEpochCommit(commit, setup) 111 require.Error(t, err) 112 }) 113 114 t.Run("missing dkg group key", func(t *testing.T) { 115 _, result, _ := unittest.BootstrapFixture(participants) 116 setup := result.ServiceEvents[0].Event.(*flow.EpochSetup) 117 commit := result.ServiceEvents[1].Event.(*flow.EpochCommit) 118 commit.DKGGroupKey = nil 119 120 err := protocol.IsValidEpochCommit(commit, setup) 121 require.Error(t, err) 122 }) 123 124 t.Run("inconsistent DKG participants", func(t *testing.T) { 125 _, result, _ := unittest.BootstrapFixture(participants) 126 setup := result.ServiceEvents[0].Event.(*flow.EpochSetup) 127 commit := result.ServiceEvents[1].Event.(*flow.EpochCommit) 128 // add an extra DKG participant key 129 commit.DKGParticipantKeys = append(commit.DKGParticipantKeys, unittest.KeyFixture(crypto.BLSBLS12381).PublicKey()) 130 131 err := protocol.IsValidEpochCommit(commit, setup) 132 require.Error(t, err) 133 }) 134 } 135 136 // TestIsValidExtendingEpochSetup tests that implementation enforces the following protocol rules in case they are violated: 137 // (a) We should only have a single epoch setup event per epoch. 138 // (b) The setup event should have the counter increased by one 139 // (c) The first view needs to be exactly one greater than the current epoch final view 140 // additionally we require other conditions, but they are tested by separate test `TestEpochSetupValidity`. 141 func TestIsValidExtendingEpochSetup(t *testing.T) { 142 t.Run("happy path", func(t *testing.T) { 143 protocolState := unittest.EpochStateFixture() 144 currentEpochSetup := protocolState.CurrentEpochSetup 145 extendingSetup := unittest.EpochSetupFixture( 146 unittest.WithFirstView(currentEpochSetup.FinalView+1), 147 unittest.WithFinalView(currentEpochSetup.FinalView+1000), 148 unittest.SetupWithCounter(currentEpochSetup.Counter+1), 149 unittest.WithParticipants(participants.ToSkeleton()), 150 ) 151 err := protocol.IsValidExtendingEpochSetup(extendingSetup, protocolState.ProtocolStateEntry, currentEpochSetup) 152 require.NoError(t, err) 153 }) 154 t.Run("(a) We should only have a single epoch setup event per epoch.", func(t *testing.T) { 155 protocolState := unittest.EpochStateFixture(unittest.WithNextEpochProtocolState()) 156 currentEpochSetup := protocolState.CurrentEpochSetup 157 extendingSetup := unittest.EpochSetupFixture( 158 unittest.WithFirstView(currentEpochSetup.FinalView+1), 159 unittest.WithFinalView(currentEpochSetup.FinalView+1000), 160 unittest.SetupWithCounter(currentEpochSetup.Counter+1), 161 unittest.WithParticipants(participants.ToSkeleton()), 162 ) 163 err := protocol.IsValidExtendingEpochSetup(extendingSetup, protocolState.ProtocolStateEntry, currentEpochSetup) 164 require.Error(t, err) 165 }) 166 t.Run("(b) The setup event should have the counter increased by one", func(t *testing.T) { 167 protocolState := unittest.EpochStateFixture() 168 currentEpochSetup := protocolState.CurrentEpochSetup 169 extendingSetup := unittest.EpochSetupFixture( 170 unittest.WithFirstView(currentEpochSetup.FinalView+1), 171 unittest.WithFinalView(currentEpochSetup.FinalView+1000), 172 unittest.SetupWithCounter(currentEpochSetup.Counter+2), 173 unittest.WithParticipants(participants.ToSkeleton()), 174 ) 175 err := protocol.IsValidExtendingEpochSetup(extendingSetup, protocolState.ProtocolStateEntry, currentEpochSetup) 176 require.Error(t, err) 177 }) 178 t.Run("(c) The first view needs to be exactly one greater than the current epoch final view", func(t *testing.T) { 179 protocolState := unittest.EpochStateFixture() 180 currentEpochSetup := protocolState.CurrentEpochSetup 181 extendingSetup := unittest.EpochSetupFixture( 182 unittest.WithFirstView(currentEpochSetup.FinalView+2), 183 unittest.WithFinalView(currentEpochSetup.FinalView+1000), 184 unittest.SetupWithCounter(currentEpochSetup.Counter+1), 185 unittest.WithParticipants(participants.ToSkeleton()), 186 ) 187 err := protocol.IsValidExtendingEpochSetup(extendingSetup, protocolState.ProtocolStateEntry, currentEpochSetup) 188 require.Error(t, err) 189 }) 190 } 191 192 // TestIsValidExtendingEpochCommit tests that implementation enforces the following protocol rules in case they are violated: 193 // (a) The epoch setup event needs to happen before the commit. 194 // (b) We should only have a single epoch commit event per epoch. 195 // additionally we require other conditions, but they are tested by separate test `TestEpochCommitValidity`. 196 func TestIsValidExtendingEpochCommit(t *testing.T) { 197 t.Run("happy path", func(t *testing.T) { 198 protocolState := unittest.EpochStateFixture(unittest.WithNextEpochProtocolState(), func(entry *flow.RichProtocolStateEntry) { 199 entry.NextEpochCommit = nil 200 entry.NextEpoch.CommitID = flow.ZeroID 201 }) 202 203 nextEpochSetup := protocolState.NextEpochSetup 204 extendingSetup := unittest.EpochCommitFixture( 205 unittest.CommitWithCounter(nextEpochSetup.Counter), 206 unittest.WithDKGFromParticipants(nextEpochSetup.Participants), 207 ) 208 err := protocol.IsValidExtendingEpochCommit(extendingSetup, protocolState.ProtocolStateEntry, nextEpochSetup) 209 require.NoError(t, err) 210 }) 211 t.Run("(a) The epoch setup event needs to happen before the commit", func(t *testing.T) { 212 protocolState := unittest.EpochStateFixture() 213 currentEpochSetup := protocolState.CurrentEpochSetup 214 nextEpochSetup := unittest.EpochSetupFixture( 215 unittest.WithFirstView(currentEpochSetup.FinalView+1), 216 unittest.WithFinalView(currentEpochSetup.FinalView+1000), 217 unittest.SetupWithCounter(currentEpochSetup.Counter+1), 218 unittest.WithParticipants(participants.ToSkeleton()), 219 ) 220 extendingSetup := unittest.EpochCommitFixture( 221 unittest.CommitWithCounter(nextEpochSetup.Counter), 222 unittest.WithDKGFromParticipants(nextEpochSetup.Participants), 223 ) 224 err := protocol.IsValidExtendingEpochCommit(extendingSetup, protocolState.ProtocolStateEntry, nextEpochSetup) 225 require.Error(t, err) 226 }) 227 t.Run("We should only have a single epoch commit event per epoch", func(t *testing.T) { 228 protocolState := unittest.EpochStateFixture(unittest.WithNextEpochProtocolState()) 229 230 nextEpochSetup := protocolState.NextEpochSetup 231 extendingSetup := unittest.EpochCommitFixture( 232 unittest.CommitWithCounter(nextEpochSetup.Counter), 233 unittest.WithDKGFromParticipants(nextEpochSetup.Participants), 234 ) 235 err := protocol.IsValidExtendingEpochCommit(extendingSetup, protocolState.ProtocolStateEntry, nextEpochSetup) 236 require.Error(t, err) 237 }) 238 }