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