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  }