github.com/ari-anchor/sei-tendermint@v0.0.0-20230519144642-dc826b7b56bb/internal/state/validation_test.go (about)

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