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