github.com/ari-anchor/sei-tendermint@v0.0.0-20230519144642-dc826b7b56bb/internal/state/validation_test.go (about) 1 package state_test 2 3 import ( 4 "context" 5 "testing" 6 "time" 7 8 "github.com/stretchr/testify/assert" 9 "github.com/stretchr/testify/mock" 10 "github.com/stretchr/testify/require" 11 dbm "github.com/tendermint/tm-db" 12 13 abciclient "github.com/ari-anchor/sei-tendermint/abci/client" 14 abci "github.com/ari-anchor/sei-tendermint/abci/types" 15 "github.com/ari-anchor/sei-tendermint/crypto" 16 "github.com/ari-anchor/sei-tendermint/crypto/ed25519" 17 "github.com/ari-anchor/sei-tendermint/internal/eventbus" 18 mpmocks "github.com/ari-anchor/sei-tendermint/internal/mempool/mocks" 19 "github.com/ari-anchor/sei-tendermint/internal/proxy" 20 sm "github.com/ari-anchor/sei-tendermint/internal/state" 21 "github.com/ari-anchor/sei-tendermint/internal/state/mocks" 22 statefactory "github.com/ari-anchor/sei-tendermint/internal/state/test/factory" 23 "github.com/ari-anchor/sei-tendermint/internal/store" 24 testfactory "github.com/ari-anchor/sei-tendermint/internal/test/factory" 25 "github.com/ari-anchor/sei-tendermint/libs/log" 26 tmtime "github.com/ari-anchor/sei-tendermint/libs/time" 27 tmproto "github.com/ari-anchor/sei-tendermint/proto/tendermint/types" 28 "github.com/ari-anchor/sei-tendermint/types" 29 ) 30 31 const validationTestsStopHeight int64 = 10 32 33 func TestValidateBlockHeader(t *testing.T) { 34 ctx, cancel := context.WithCancel(context.Background()) 35 defer cancel() 36 logger := log.NewNopLogger() 37 proxyApp := proxy.New(abciclient.NewLocalClient(logger, &testApp{}), logger, proxy.NopMetrics()) 38 require.NoError(t, proxyApp.Start(ctx)) 39 40 eventBus := eventbus.NewDefault(logger) 41 require.NoError(t, eventBus.Start(ctx)) 42 43 state, stateDB, privVals := makeState(t, 3, 1) 44 stateStore := sm.NewStore(stateDB) 45 mp := &mpmocks.Mempool{} 46 mp.On("Lock").Return() 47 mp.On("Unlock").Return() 48 mp.On("FlushAppConn", mock.Anything).Return(nil) 49 mp.On("Update", 50 mock.Anything, 51 mock.Anything, 52 mock.Anything, 53 mock.Anything, 54 mock.Anything, 55 mock.Anything, 56 mock.Anything).Return(nil) 57 mp.On("TxStore").Return(nil) 58 59 blockStore := store.NewBlockStore(dbm.NewMemDB()) 60 blockExec := sm.NewBlockExecutor( 61 stateStore, 62 logger, 63 proxyApp, 64 mp, 65 sm.EmptyEvidencePool{}, 66 blockStore, 67 eventBus, 68 sm.NopMetrics(), 69 ) 70 lastCommit := &types.Commit{} 71 var lastExtCommit *types.ExtendedCommit 72 73 // some bad values 74 wrongHash := crypto.Checksum([]byte("this hash is wrong")) 75 wrongVersion1 := state.Version.Consensus 76 wrongVersion1.Block += 2 77 wrongVersion2 := state.Version.Consensus 78 wrongVersion2.App += 2 79 80 // Manipulation of any header field causes failure. 81 testCases := []struct { 82 name string 83 malleateBlock func(block *types.Block) 84 }{ 85 {"Version wrong1", func(block *types.Block) { block.Version = wrongVersion1 }}, 86 {"Version wrong2", func(block *types.Block) { block.Version = wrongVersion2 }}, 87 {"ChainID wrong", func(block *types.Block) { block.ChainID = "not-the-real-one" }}, 88 {"Height wrong", func(block *types.Block) { block.Height += 10 }}, 89 {"Time wrong", func(block *types.Block) { block.Time = block.Time.Add(-time.Second * 1) }}, 90 91 {"LastBlockID wrong", func(block *types.Block) { block.LastBlockID.PartSetHeader.Total += 10 }}, 92 {"LastCommitHash wrong", func(block *types.Block) { block.LastCommitHash = wrongHash }}, 93 {"DataHash wrong", func(block *types.Block) { block.DataHash = wrongHash }}, 94 95 {"ValidatorsHash wrong", func(block *types.Block) { block.ValidatorsHash = wrongHash }}, 96 {"NextValidatorsHash wrong", func(block *types.Block) { block.NextValidatorsHash = wrongHash }}, 97 {"ConsensusHash wrong", func(block *types.Block) { block.ConsensusHash = wrongHash }}, 98 {"AppHash wrong", func(block *types.Block) { block.AppHash = wrongHash }}, 99 {"LastResultsHash wrong", func(block *types.Block) { block.LastResultsHash = wrongHash }}, 100 101 {"EvidenceHash wrong", func(block *types.Block) { block.EvidenceHash = wrongHash }}, 102 {"Proposer wrong", func(block *types.Block) { block.ProposerAddress = ed25519.GenPrivKey().PubKey().Address() }}, 103 {"Proposer invalid", func(block *types.Block) { block.ProposerAddress = []byte("wrong size") }}, 104 105 {"first LastCommit contains signatures", func(block *types.Block) { 106 block.LastCommit = &types.Commit{Signatures: []types.CommitSig{types.NewCommitSigAbsent()}} 107 block.LastCommitHash = block.LastCommit.Hash() 108 }}, 109 } 110 111 // Build up state for multiple heights 112 for height := int64(1); height < validationTestsStopHeight; height++ { 113 /* 114 Invalid blocks don't pass 115 */ 116 for _, tc := range testCases { 117 block := statefactory.MakeBlock(state, height, lastCommit) 118 tc.malleateBlock(block) 119 err := blockExec.ValidateBlock(ctx, state, block) 120 t.Logf("%s: %v", tc.name, err) 121 require.Error(t, err, tc.name) 122 } 123 124 /* 125 A good block passes 126 */ 127 state, _, lastExtCommit = makeAndCommitGoodBlock(ctx, t, 128 state, height, lastCommit, state.Validators.GetProposer().Address, blockExec, privVals, nil) 129 lastCommit = lastExtCommit.ToCommit() 130 } 131 132 nextHeight := validationTestsStopHeight 133 block := statefactory.MakeBlock(state, nextHeight, lastCommit) 134 state.InitialHeight = nextHeight + 1 135 err := blockExec.ValidateBlock(ctx, state, block) 136 require.Error(t, err, "expected an error when state is ahead of block") 137 assert.Contains(t, err.Error(), "lower than initial height") 138 } 139 140 func TestValidateBlockCommit(t *testing.T) { 141 ctx, cancel := context.WithCancel(context.Background()) 142 defer cancel() 143 144 logger := log.NewNopLogger() 145 proxyApp := proxy.New(abciclient.NewLocalClient(logger, &testApp{}), logger, proxy.NopMetrics()) 146 require.NoError(t, proxyApp.Start(ctx)) 147 148 eventBus := eventbus.NewDefault(logger) 149 require.NoError(t, eventBus.Start(ctx)) 150 151 state, stateDB, privVals := makeState(t, 1, 1) 152 stateStore := sm.NewStore(stateDB) 153 mp := &mpmocks.Mempool{} 154 mp.On("Lock").Return() 155 mp.On("Unlock").Return() 156 mp.On("FlushAppConn", mock.Anything).Return(nil) 157 mp.On("Update", 158 mock.Anything, 159 mock.Anything, 160 mock.Anything, 161 mock.Anything, 162 mock.Anything, 163 mock.Anything, 164 mock.Anything).Return(nil) 165 mp.On("TxStore").Return(nil) 166 167 blockStore := store.NewBlockStore(dbm.NewMemDB()) 168 blockExec := sm.NewBlockExecutor( 169 stateStore, 170 logger, 171 proxyApp, 172 mp, 173 sm.EmptyEvidencePool{}, 174 blockStore, 175 eventBus, 176 sm.NopMetrics(), 177 ) 178 lastCommit := &types.Commit{} 179 var lastExtCommit *types.ExtendedCommit 180 wrongSigsCommit := &types.Commit{Height: 1} 181 badPrivVal := types.NewMockPV() 182 183 for height := int64(1); height < validationTestsStopHeight; height++ { 184 proposerAddr := state.Validators.GetProposer().Address 185 if height > 1 { 186 /* 187 #2589: ensure state.LastValidators.VerifyCommit fails here 188 */ 189 // should be height-1 instead of height 190 wrongHeightVote, err := testfactory.MakeVote( 191 ctx, 192 privVals[proposerAddr.String()], 193 chainID, 194 1, 195 height, 196 0, 197 2, 198 state.LastBlockID, 199 time.Now(), 200 ) 201 require.NoError(t, err) 202 wrongHeightCommit := &types.Commit{ 203 Height: wrongHeightVote.Height, 204 Round: wrongHeightVote.Round, 205 BlockID: state.LastBlockID, 206 Signatures: []types.CommitSig{wrongHeightVote.CommitSig()}, 207 } 208 block := statefactory.MakeBlock(state, height, wrongHeightCommit) 209 err = blockExec.ValidateBlock(ctx, state, block) 210 _, isErrInvalidCommitHeight := err.(types.ErrInvalidCommitHeight) 211 require.True(t, isErrInvalidCommitHeight, "expected ErrInvalidCommitHeight at height %d but got: %v", height, err) 212 213 /* 214 #2589: test len(block.LastCommit.Signatures) == state.LastValidators.Size() 215 */ 216 block = statefactory.MakeBlock(state, height, wrongSigsCommit) 217 err = blockExec.ValidateBlock(ctx, state, block) 218 _, isErrInvalidCommitSignatures := err.(types.ErrInvalidCommitSignatures) 219 require.True(t, isErrInvalidCommitSignatures, 220 "expected ErrInvalidCommitSignatures at height %d, but got: %v", 221 height, 222 err, 223 ) 224 } 225 226 /* 227 A good block passes 228 */ 229 var blockID types.BlockID 230 state, blockID, lastExtCommit = makeAndCommitGoodBlock( 231 ctx, 232 t, 233 state, 234 height, 235 lastCommit, 236 proposerAddr, 237 blockExec, 238 privVals, 239 nil, 240 ) 241 lastCommit = lastExtCommit.ToCommit() 242 243 /* 244 wrongSigsCommit is fine except for the extra bad precommit 245 */ 246 goodVote, err := testfactory.MakeVote( 247 ctx, 248 privVals[proposerAddr.String()], 249 chainID, 250 1, 251 height, 252 0, 253 2, 254 blockID, 255 time.Now(), 256 ) 257 require.NoError(t, err) 258 bpvPubKey, err := badPrivVal.GetPubKey(ctx) 259 require.NoError(t, err) 260 261 badVote := &types.Vote{ 262 ValidatorAddress: bpvPubKey.Address(), 263 ValidatorIndex: 0, 264 Height: height, 265 Round: 0, 266 Timestamp: tmtime.Now(), 267 Type: tmproto.PrecommitType, 268 BlockID: blockID, 269 } 270 271 g := goodVote.ToProto() 272 b := badVote.ToProto() 273 274 err = badPrivVal.SignVote(ctx, chainID, g) 275 require.NoError(t, err, "height %d", height) 276 err = badPrivVal.SignVote(ctx, chainID, b) 277 require.NoError(t, err, "height %d", height) 278 279 goodVote.Signature, badVote.Signature = g.Signature, b.Signature 280 281 wrongSigsCommit = &types.Commit{ 282 Height: goodVote.Height, 283 Round: goodVote.Round, 284 BlockID: blockID, 285 Signatures: []types.CommitSig{goodVote.CommitSig(), badVote.CommitSig()}, 286 } 287 } 288 } 289 290 func TestValidateBlockEvidence(t *testing.T) { 291 ctx, cancel := context.WithCancel(context.Background()) 292 defer cancel() 293 294 logger := log.NewNopLogger() 295 proxyApp := proxy.New(abciclient.NewLocalClient(logger, &testApp{}), logger, proxy.NopMetrics()) 296 require.NoError(t, proxyApp.Start(ctx)) 297 298 state, stateDB, privVals := makeState(t, 4, 1) 299 stateStore := sm.NewStore(stateDB) 300 blockStore := store.NewBlockStore(dbm.NewMemDB()) 301 defaultEvidenceTime := time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC) 302 303 evpool := &mocks.EvidencePool{} 304 evpool.On("CheckEvidence", ctx, mock.AnythingOfType("types.EvidenceList")).Return(nil) 305 evpool.On("Update", ctx, mock.AnythingOfType("state.State"), mock.AnythingOfType("types.EvidenceList")).Return() 306 evpool.On("ABCIEvidence", mock.AnythingOfType("int64"), mock.AnythingOfType("[]types.Evidence")).Return( 307 []abci.Misbehavior{}) 308 309 eventBus := eventbus.NewDefault(logger) 310 require.NoError(t, eventBus.Start(ctx)) 311 mp := &mpmocks.Mempool{} 312 mp.On("Lock").Return() 313 mp.On("Unlock").Return() 314 mp.On("FlushAppConn", mock.Anything).Return(nil) 315 mp.On("Update", 316 mock.Anything, 317 mock.Anything, 318 mock.Anything, 319 mock.Anything, 320 mock.Anything, 321 mock.Anything, 322 mock.Anything).Return(nil) 323 mp.On("TxStore").Return(nil) 324 325 state.ConsensusParams.Evidence.MaxBytes = 1000 326 blockExec := sm.NewBlockExecutor( 327 stateStore, 328 log.NewNopLogger(), 329 proxyApp, 330 mp, 331 evpool, 332 blockStore, 333 eventBus, 334 sm.NopMetrics(), 335 ) 336 lastCommit := &types.Commit{} 337 var lastExtCommit *types.ExtendedCommit 338 339 for height := int64(1); height < validationTestsStopHeight; height++ { 340 proposerAddr := state.Validators.GetProposer().Address 341 maxBytesEvidence := state.ConsensusParams.Evidence.MaxBytes 342 if height > 1 { 343 /* 344 A block with too much evidence fails 345 */ 346 evidence := make([]types.Evidence, 0) 347 var currentBytes int64 348 // more bytes than the maximum allowed for evidence 349 for currentBytes <= maxBytesEvidence { 350 newEv, err := types.NewMockDuplicateVoteEvidenceWithValidator(ctx, height, time.Now(), 351 privVals[proposerAddr.String()], chainID) 352 require.NoError(t, err) 353 evidence = append(evidence, newEv) 354 currentBytes += int64(len(newEv.Bytes())) 355 } 356 block := state.MakeBlock(height, testfactory.MakeNTxs(height, 10), lastCommit, evidence, proposerAddr) 357 358 err := blockExec.ValidateBlock(ctx, state, block) 359 if assert.Error(t, err) { 360 _, ok := err.(*types.ErrEvidenceOverflow) 361 require.True(t, ok, "expected error to be of type ErrEvidenceOverflow at height %d but got %v", height, err) 362 } 363 } 364 365 /* 366 A good block with several pieces of good evidence passes 367 */ 368 evidence := make([]types.Evidence, 0) 369 var currentBytes int64 370 // precisely the amount of allowed evidence 371 for { 372 newEv, err := types.NewMockDuplicateVoteEvidenceWithValidator(ctx, height, defaultEvidenceTime, 373 privVals[proposerAddr.String()], chainID) 374 require.NoError(t, err) 375 currentBytes += int64(len(newEv.Bytes())) 376 if currentBytes >= maxBytesEvidence { 377 break 378 } 379 evidence = append(evidence, newEv) 380 } 381 382 state, _, lastExtCommit = makeAndCommitGoodBlock( 383 ctx, 384 t, 385 state, 386 height, 387 lastCommit, 388 proposerAddr, 389 blockExec, 390 privVals, 391 evidence, 392 ) 393 lastCommit = lastExtCommit.ToCommit() 394 395 } 396 }