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