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  }