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  }