github.com/badrootd/celestia-core@v0.0.0-20240305091328-aa4207a4b25d/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 abci "github.com/badrootd/celestia-core/abci/types" 12 "github.com/badrootd/celestia-core/crypto/ed25519" 13 "github.com/badrootd/celestia-core/crypto/tmhash" 14 "github.com/badrootd/celestia-core/libs/log" 15 memmock "github.com/badrootd/celestia-core/mempool/mock" 16 cmtproto "github.com/badrootd/celestia-core/proto/tendermint/types" 17 sm "github.com/badrootd/celestia-core/state" 18 "github.com/badrootd/celestia-core/state/mocks" 19 "github.com/badrootd/celestia-core/test/factory" 20 "github.com/badrootd/celestia-core/types" 21 cmttime "github.com/badrootd/celestia-core/types/time" 22 ) 23 24 const validationTestsStopHeight int64 = 10 25 26 func TestValidateBlockHeader(t *testing.T) { 27 proxyApp := newTestApp() 28 require.NoError(t, proxyApp.Start()) 29 defer proxyApp.Stop() //nolint:errcheck // ignore for tests 30 31 state, stateDB, privVals := makeState(3, 1) 32 stateStore := sm.NewStore(stateDB, sm.StoreOptions{ 33 DiscardABCIResponses: false, 34 }) 35 blockExec := sm.NewBlockExecutor( 36 stateStore, 37 log.TestingLogger(), 38 proxyApp.Consensus(), 39 memmock.Mempool{}, 40 sm.EmptyEvidencePool{}, 41 ) 42 lastCommit := types.NewCommit(0, 0, types.BlockID{}, nil) 43 44 // some bad values 45 wrongHash := tmhash.Sum([]byte("this hash is wrong")) 46 wrongVersion1 := state.Version.Consensus 47 wrongVersion1.Block += 2 48 wrongVersion2 := state.Version.Consensus 49 wrongVersion2.App += 2 50 51 // Manipulation of any header field causes failure. 52 testCases := []struct { 53 name string 54 malleateBlock func(block *types.Block) 55 }{ 56 {"Version wrong1", func(block *types.Block) { block.Version = wrongVersion1 }}, 57 {"Version wrong2", func(block *types.Block) { block.Version = wrongVersion2 }}, 58 {"ChainID wrong", func(block *types.Block) { block.ChainID = "not-the-real-one" }}, 59 {"Height wrong", func(block *types.Block) { block.Height += 10 }}, 60 {"Time wrong", func(block *types.Block) { block.Time = block.Time.Add(-time.Second * 1) }}, 61 62 {"LastBlockID wrong", func(block *types.Block) { block.LastBlockID.PartSetHeader.Total += 10 }}, 63 {"LastCommitHash wrong", func(block *types.Block) { block.LastCommitHash = wrongHash }}, 64 65 {"ValidatorsHash wrong", func(block *types.Block) { block.ValidatorsHash = wrongHash }}, 66 {"NextValidatorsHash wrong", func(block *types.Block) { block.NextValidatorsHash = wrongHash }}, 67 {"ConsensusHash wrong", func(block *types.Block) { block.ConsensusHash = wrongHash }}, 68 {"AppHash wrong", func(block *types.Block) { block.AppHash = wrongHash }}, 69 {"LastResultsHash wrong", func(block *types.Block) { block.LastResultsHash = wrongHash }}, 70 71 {"EvidenceHash wrong", func(block *types.Block) { block.EvidenceHash = wrongHash }}, 72 {"Proposer wrong", func(block *types.Block) { block.ProposerAddress = ed25519.GenPrivKey().PubKey().Address() }}, 73 {"Proposer invalid", func(block *types.Block) { block.ProposerAddress = []byte("wrong size") }}, 74 } 75 76 // Build up state for multiple heights 77 for height := int64(1); height < validationTestsStopHeight; height++ { 78 proposerAddr := state.Validators.GetProposer().Address 79 /* 80 Invalid blocks don't pass 81 */ 82 for _, tc := range testCases { 83 block, _ := state.MakeBlock(height, makeTxs(height), lastCommit, nil, proposerAddr) 84 tc.malleateBlock(block) 85 err := blockExec.ValidateBlock(state, block) 86 require.Error(t, err, tc.name) 87 } 88 89 /* 90 A good block passes 91 */ 92 var err error 93 state, _, lastCommit, err = makeAndCommitGoodBlock(state, height, lastCommit, proposerAddr, blockExec, privVals, nil) 94 require.NoError(t, err, "height %d", height) 95 } 96 97 nextHeight := validationTestsStopHeight 98 block, _ := state.MakeBlock( 99 nextHeight, 100 factory.MakeTenTxs(nextHeight), 101 lastCommit, 102 nil, 103 state.Validators.GetProposer().Address, 104 ) 105 state.InitialHeight = nextHeight + 1 106 err := blockExec.ValidateBlock(state, block) 107 require.Error(t, err, "expected an error when state is ahead of block") 108 assert.Contains(t, err.Error(), "lower than initial height") 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.GetProposer().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 block, _ := state.MakeBlock( 154 height, 155 factory.MakeTenTxs(height), 156 wrongHeightCommit, 157 nil, 158 proposerAddr, 159 ) 160 err = blockExec.ValidateBlock(state, block) 161 _, isErrInvalidCommitHeight := err.(types.ErrInvalidCommitHeight) 162 require.True(t, isErrInvalidCommitHeight, "expected ErrInvalidCommitHeight at height %d but got: %v", height, err) 163 164 /* 165 #2589: test len(block.LastCommit.Signatures) == state.LastValidators.Size() 166 */ 167 block, _ = state.MakeBlock( 168 height, 169 factory.MakeTenTxs(height), 170 wrongSigsCommit, 171 nil, 172 proposerAddr, 173 ) 174 err = blockExec.ValidateBlock(state, block) 175 _, isErrInvalidCommitSignatures := err.(types.ErrInvalidCommitSignatures) 176 require.True(t, isErrInvalidCommitSignatures, 177 "expected ErrInvalidCommitSignatures at height %d, but got: %v", 178 height, 179 err, 180 ) 181 } 182 183 /* 184 A good block passes 185 */ 186 var err error 187 var blockID types.BlockID 188 state, blockID, lastCommit, err = makeAndCommitGoodBlock( 189 state, 190 height, 191 lastCommit, 192 proposerAddr, 193 blockExec, 194 privVals, 195 nil, 196 ) 197 require.NoError(t, err, "height %d", height) 198 199 /* 200 wrongSigsCommit is fine except for the extra bad precommit 201 */ 202 goodVote, err := types.MakeVote(height, 203 blockID, 204 state.Validators, 205 privVals[proposerAddr.String()], 206 chainID, 207 time.Now(), 208 ) 209 require.NoError(t, err, "height %d", height) 210 211 bpvPubKey, err := badPrivVal.GetPubKey() 212 require.NoError(t, err) 213 214 badVote := &types.Vote{ 215 ValidatorAddress: bpvPubKey.Address(), 216 ValidatorIndex: 0, 217 Height: height, 218 Round: 0, 219 Timestamp: cmttime.Now(), 220 Type: cmtproto.PrecommitType, 221 BlockID: blockID, 222 } 223 224 g := goodVote.ToProto() 225 b := badVote.ToProto() 226 227 err = badPrivVal.SignVote(chainID, g) 228 require.NoError(t, err, "height %d", height) 229 err = badPrivVal.SignVote(chainID, b) 230 require.NoError(t, err, "height %d", height) 231 232 goodVote.Signature, badVote.Signature = g.Signature, b.Signature 233 234 wrongSigsCommit = types.NewCommit(goodVote.Height, goodVote.Round, 235 blockID, []types.CommitSig{goodVote.CommitSig(), badVote.CommitSig()}) 236 } 237 } 238 239 // TODO potentially delete 240 func TestValidateBlockEvidence(t *testing.T) { 241 proxyApp := newTestApp() 242 require.NoError(t, proxyApp.Start()) 243 defer proxyApp.Stop() //nolint:errcheck // ignore for tests 244 245 state, stateDB, privVals := makeState(4, 1) 246 stateStore := sm.NewStore(stateDB, sm.StoreOptions{ 247 DiscardABCIResponses: false, 248 }) 249 defaultEvidenceTime := time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC) 250 251 evpool := &mocks.EvidencePool{} 252 evpool.On("CheckEvidence", mock.AnythingOfType("types.EvidenceList")).Return(nil) 253 evpool.On("Update", mock.AnythingOfType("state.State"), mock.AnythingOfType("types.EvidenceList")).Return() 254 evpool.On("ABCIEvidence", mock.AnythingOfType("int64"), mock.AnythingOfType("[]types.Evidence")).Return( 255 []abci.Misbehavior{}) 256 257 state.ConsensusParams.Evidence.MaxBytes = 1000 258 blockExec := sm.NewBlockExecutor( 259 stateStore, 260 log.TestingLogger(), 261 proxyApp.Consensus(), 262 memmock.Mempool{}, 263 evpool, 264 ) 265 lastCommit := types.NewCommit(0, 0, types.BlockID{}, nil) 266 267 for height := int64(1); height < validationTestsStopHeight; height++ { 268 proposerAddr := state.Validators.GetProposer().Address 269 maxBytesEvidence := state.ConsensusParams.Evidence.MaxBytes 270 if height > 1 { 271 /* 272 A block with too much evidence fails 273 */ 274 evidence := make([]types.Evidence, 0) 275 var currentBytes int64 276 // more bytes than the maximum allowed for evidence 277 for currentBytes <= maxBytesEvidence { 278 newEv := types.NewMockDuplicateVoteEvidenceWithValidator(height, time.Now(), 279 privVals[proposerAddr.String()], chainID) 280 evidence = append(evidence, newEv) 281 currentBytes += int64(len(newEv.Bytes())) 282 } 283 block, _ := state.MakeBlock( 284 height, 285 factory.MakeTenTxs(height), 286 lastCommit, 287 evidence, 288 proposerAddr, 289 ) 290 err := blockExec.ValidateBlock(state, block) 291 if assert.Error(t, err) { 292 _, ok := err.(*types.ErrEvidenceOverflow) 293 require.True(t, ok, "expected error to be of type ErrEvidenceOverflow at height %d but got %v", height, err) 294 } 295 } 296 297 /* 298 A good block with several pieces of good evidence passes 299 */ 300 evidence := make([]types.Evidence, 0) 301 var currentBytes int64 302 // precisely the amount of allowed evidence 303 for { 304 newEv := types.NewMockDuplicateVoteEvidenceWithValidator(height, defaultEvidenceTime, 305 privVals[proposerAddr.String()], chainID) 306 currentBytes += int64(len(newEv.Bytes())) 307 if currentBytes >= maxBytesEvidence { 308 break 309 } 310 evidence = append(evidence, newEv) 311 } 312 313 var err error 314 state, _, lastCommit, err = makeAndCommitGoodBlock( 315 state, 316 height, 317 lastCommit, 318 proposerAddr, 319 blockExec, 320 privVals, 321 evidence, 322 ) 323 require.NoError(t, err, "height %d", height) 324 } 325 }