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 }