github.com/onflow/flow-go@v0.33.17/state/protocol/badger/validity_test.go (about)

     1  package badger
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/stretchr/testify/require"
     7  
     8  	"github.com/onflow/flow-go/crypto"
     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/state/protocol/mock"
    13  	"github.com/onflow/flow-go/utils/unittest"
    14  )
    15  
    16  var participants = unittest.IdentityListFixture(20, unittest.WithAllRoles())
    17  
    18  func TestEpochSetupValidity(t *testing.T) {
    19  	t.Run("invalid first/final view", func(t *testing.T) {
    20  		_, result, _ := unittest.BootstrapFixture(participants)
    21  		setup := result.ServiceEvents[0].Event.(*flow.EpochSetup)
    22  		// set an invalid final view for the first epoch
    23  		setup.FinalView = setup.FirstView
    24  
    25  		err := verifyEpochSetup(setup, true)
    26  		require.Error(t, err)
    27  	})
    28  
    29  	t.Run("non-canonically ordered identities", func(t *testing.T) {
    30  		_, result, _ := unittest.BootstrapFixture(participants)
    31  		setup := result.ServiceEvents[0].Event.(*flow.EpochSetup)
    32  		// randomly shuffle the identities so they are not canonically ordered
    33  		var err error
    34  		setup.Participants, err = setup.Participants.Shuffle()
    35  		require.NoError(t, err)
    36  		err = verifyEpochSetup(setup, true)
    37  		require.Error(t, err)
    38  	})
    39  
    40  	t.Run("invalid cluster assignments", func(t *testing.T) {
    41  		_, result, _ := unittest.BootstrapFixture(participants)
    42  		setup := result.ServiceEvents[0].Event.(*flow.EpochSetup)
    43  		// create an invalid cluster assignment (node appears in multiple clusters)
    44  		collector := participants.Filter(filter.HasRole(flow.RoleCollection))[0]
    45  		setup.Assignments = append(setup.Assignments, []flow.Identifier{collector.NodeID})
    46  
    47  		err := verifyEpochSetup(setup, true)
    48  		require.Error(t, err)
    49  	})
    50  
    51  	t.Run("short seed", func(t *testing.T) {
    52  		_, result, _ := unittest.BootstrapFixture(participants)
    53  		setup := result.ServiceEvents[0].Event.(*flow.EpochSetup)
    54  		setup.RandomSource = unittest.SeedFixture(crypto.KeyGenSeedMinLen - 1)
    55  
    56  		err := verifyEpochSetup(setup, true)
    57  		require.Error(t, err)
    58  	})
    59  }
    60  
    61  func TestBootstrapInvalidEpochCommit(t *testing.T) {
    62  	t.Run("inconsistent counter", func(t *testing.T) {
    63  		_, result, _ := unittest.BootstrapFixture(participants)
    64  		setup := result.ServiceEvents[0].Event.(*flow.EpochSetup)
    65  		commit := result.ServiceEvents[1].Event.(*flow.EpochCommit)
    66  		// use a different counter for the commit
    67  		commit.Counter = setup.Counter + 1
    68  
    69  		err := isValidEpochCommit(commit, setup)
    70  		require.Error(t, err)
    71  	})
    72  
    73  	t.Run("inconsistent cluster QCs", func(t *testing.T) {
    74  		_, result, _ := unittest.BootstrapFixture(participants)
    75  		setup := result.ServiceEvents[0].Event.(*flow.EpochSetup)
    76  		commit := result.ServiceEvents[1].Event.(*flow.EpochCommit)
    77  		// add an extra QC to commit
    78  		extraQC := unittest.QuorumCertificateWithSignerIDsFixture()
    79  		commit.ClusterQCs = append(commit.ClusterQCs, flow.ClusterQCVoteDataFromQC(extraQC))
    80  
    81  		err := isValidEpochCommit(commit, setup)
    82  		require.Error(t, err)
    83  	})
    84  
    85  	t.Run("missing dkg group key", func(t *testing.T) {
    86  		_, result, _ := unittest.BootstrapFixture(participants)
    87  		setup := result.ServiceEvents[0].Event.(*flow.EpochSetup)
    88  		commit := result.ServiceEvents[1].Event.(*flow.EpochCommit)
    89  		commit.DKGGroupKey = nil
    90  
    91  		err := isValidEpochCommit(commit, setup)
    92  		require.Error(t, err)
    93  	})
    94  
    95  	t.Run("inconsistent DKG participants", func(t *testing.T) {
    96  		_, result, _ := unittest.BootstrapFixture(participants)
    97  		setup := result.ServiceEvents[0].Event.(*flow.EpochSetup)
    98  		commit := result.ServiceEvents[1].Event.(*flow.EpochCommit)
    99  		// add an extra DKG participant key
   100  		commit.DKGParticipantKeys = append(commit.DKGParticipantKeys, unittest.KeyFixture(crypto.BLSBLS12381).PublicKey())
   101  
   102  		err := isValidEpochCommit(commit, setup)
   103  		require.Error(t, err)
   104  	})
   105  }
   106  
   107  // TestEntityExpirySnapshotValidation tests that we perform correct sanity checks when
   108  // bootstrapping consensus nodes and access nodes we expect that we only bootstrap snapshots
   109  // with sufficient history.
   110  func TestEntityExpirySnapshotValidation(t *testing.T) {
   111  	t.Run("spork-root-snapshot", func(t *testing.T) {
   112  		rootSnapshot := unittest.RootSnapshotFixture(participants)
   113  		err := ValidRootSnapshotContainsEntityExpiryRange(rootSnapshot)
   114  		require.NoError(t, err)
   115  	})
   116  	t.Run("not-enough-history", func(t *testing.T) {
   117  		rootSnapshot := unittest.RootSnapshotFixture(participants)
   118  		rootSnapshot.Encodable().Head.Height += 10 // advance height to be not spork root snapshot
   119  		err := ValidRootSnapshotContainsEntityExpiryRange(rootSnapshot)
   120  		require.Error(t, err)
   121  	})
   122  	t.Run("enough-history-spork-just-started", func(t *testing.T) {
   123  		rootSnapshot := unittest.RootSnapshotFixture(participants)
   124  		// advance height to be not spork root snapshot, but still lower than transaction expiry
   125  		rootSnapshot.Encodable().Head.Height += flow.DefaultTransactionExpiry / 2
   126  		// add blocks to sealing segment
   127  		rootSnapshot.Encodable().SealingSegment.ExtraBlocks = unittest.BlockFixtures(int(flow.DefaultTransactionExpiry / 2))
   128  		err := ValidRootSnapshotContainsEntityExpiryRange(rootSnapshot)
   129  		require.NoError(t, err)
   130  	})
   131  	t.Run("enough-history-long-spork", func(t *testing.T) {
   132  		rootSnapshot := unittest.RootSnapshotFixture(participants)
   133  		// advance height to be not spork root snapshot
   134  		rootSnapshot.Encodable().Head.Height += flow.DefaultTransactionExpiry * 2
   135  		// add blocks to sealing segment
   136  		rootSnapshot.Encodable().SealingSegment.ExtraBlocks = unittest.BlockFixtures(int(flow.DefaultTransactionExpiry) - 1)
   137  		err := ValidRootSnapshotContainsEntityExpiryRange(rootSnapshot)
   138  		require.NoError(t, err)
   139  	})
   140  	t.Run("more-history-than-needed", func(t *testing.T) {
   141  		rootSnapshot := unittest.RootSnapshotFixture(participants)
   142  		// advance height to be not spork root snapshot
   143  		rootSnapshot.Encodable().Head.Height += flow.DefaultTransactionExpiry * 2
   144  		// add blocks to sealing segment
   145  		rootSnapshot.Encodable().SealingSegment.ExtraBlocks = unittest.BlockFixtures(flow.DefaultTransactionExpiry * 2)
   146  		err := ValidRootSnapshotContainsEntityExpiryRange(rootSnapshot)
   147  		require.NoError(t, err)
   148  	})
   149  }
   150  
   151  func TestValidateVersionBeacon(t *testing.T) {
   152  	t.Run("no version beacon is ok", func(t *testing.T) {
   153  		snap := new(mock.Snapshot)
   154  
   155  		snap.On("VersionBeacon").Return(nil, nil)
   156  
   157  		err := validateVersionBeacon(snap)
   158  		require.NoError(t, err)
   159  	})
   160  	t.Run("valid version beacon is ok", func(t *testing.T) {
   161  		snap := new(mock.Snapshot)
   162  		block := unittest.BlockFixture()
   163  		block.Header.Height = 100
   164  
   165  		vb := &flow.SealedVersionBeacon{
   166  			VersionBeacon: &flow.VersionBeacon{
   167  				VersionBoundaries: []flow.VersionBoundary{
   168  					{
   169  						BlockHeight: 1000,
   170  						Version:     "1.0.0",
   171  					},
   172  				},
   173  				Sequence: 50,
   174  			},
   175  			SealHeight: uint64(37),
   176  		}
   177  
   178  		snap.On("Head").Return(block.Header, nil)
   179  		snap.On("VersionBeacon").Return(vb, nil)
   180  
   181  		err := validateVersionBeacon(snap)
   182  		require.NoError(t, err)
   183  	})
   184  	t.Run("height must be below highest block", func(t *testing.T) {
   185  		snap := new(mock.Snapshot)
   186  		block := unittest.BlockFixture()
   187  		block.Header.Height = 12
   188  
   189  		vb := &flow.SealedVersionBeacon{
   190  			VersionBeacon: &flow.VersionBeacon{
   191  				VersionBoundaries: []flow.VersionBoundary{
   192  					{
   193  						BlockHeight: 1000,
   194  						Version:     "1.0.0",
   195  					},
   196  				},
   197  				Sequence: 50,
   198  			},
   199  			SealHeight: uint64(37),
   200  		}
   201  
   202  		snap.On("Head").Return(block.Header, nil)
   203  		snap.On("VersionBeacon").Return(vb, nil)
   204  
   205  		err := validateVersionBeacon(snap)
   206  		require.Error(t, err)
   207  		require.True(t, protocol.IsInvalidServiceEventError(err))
   208  	})
   209  	t.Run("version beacon must be valid", func(t *testing.T) {
   210  		snap := new(mock.Snapshot)
   211  		block := unittest.BlockFixture()
   212  		block.Header.Height = 12
   213  
   214  		vb := &flow.SealedVersionBeacon{
   215  			VersionBeacon: &flow.VersionBeacon{
   216  				VersionBoundaries: []flow.VersionBoundary{
   217  					{
   218  						BlockHeight: 0,
   219  						Version:     "asdf", // invalid semver - hence will be considered invalid
   220  					},
   221  				},
   222  				Sequence: 50,
   223  			},
   224  			SealHeight: uint64(1),
   225  		}
   226  
   227  		snap.On("Head").Return(block.Header, nil)
   228  		snap.On("VersionBeacon").Return(vb, nil)
   229  
   230  		err := validateVersionBeacon(snap)
   231  		require.Error(t, err)
   232  		require.True(t, protocol.IsInvalidServiceEventError(err))
   233  	})
   234  }