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