github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/bft/state/validation_test.go (about)

     1  package state_test
     2  
     3  import (
     4  	"testing"
     5  	"time"
     6  
     7  	"github.com/stretchr/testify/require"
     8  
     9  	"github.com/gnolang/gno/tm2/pkg/bft/mempool/mock"
    10  	sm "github.com/gnolang/gno/tm2/pkg/bft/state"
    11  	"github.com/gnolang/gno/tm2/pkg/bft/types"
    12  	tmtime "github.com/gnolang/gno/tm2/pkg/bft/types/time"
    13  	"github.com/gnolang/gno/tm2/pkg/crypto"
    14  	"github.com/gnolang/gno/tm2/pkg/crypto/ed25519"
    15  	"github.com/gnolang/gno/tm2/pkg/crypto/tmhash"
    16  	"github.com/gnolang/gno/tm2/pkg/log"
    17  )
    18  
    19  const validationTestsStopHeight int64 = 10
    20  
    21  func TestValidateBlockHeader(t *testing.T) {
    22  	t.Parallel()
    23  
    24  	proxyApp := newTestApp()
    25  	require.NoError(t, proxyApp.Start())
    26  	defer proxyApp.Stop()
    27  
    28  	state, stateDB, privVals := makeState(3, 1)
    29  	blockExec := sm.NewBlockExecutor(stateDB, log.NewTestingLogger(t), proxyApp.Consensus(), mock.Mempool{})
    30  	lastCommit := types.NewCommit(types.BlockID{}, nil)
    31  
    32  	// some bad values
    33  	wrongHash := tmhash.Sum([]byte("this hash is wrong"))
    34  
    35  	// Manipulation of any header field causes failure.
    36  	testCases := []struct {
    37  		name          string
    38  		malleateBlock func(block *types.Block)
    39  	}{
    40  		{"BlockVersion wrong", func(block *types.Block) { block.Version += "-wrong" }},
    41  		{"AppVersion wrong", func(block *types.Block) { block.AppVersion += "-wrong" }},
    42  		{"ChainID wrong", func(block *types.Block) { block.ChainID = "not-the-real-one" }},
    43  		{"Height wrong", func(block *types.Block) { block.Height += 10 }},
    44  		{"Time wrong", func(block *types.Block) { block.Time = block.Time.Add(-time.Second * 1) }},
    45  		{"NumTxs wrong", func(block *types.Block) { block.NumTxs += 10 }},
    46  		{"TotalTxs wrong", func(block *types.Block) { block.TotalTxs += 10 }},
    47  
    48  		{"LastBlockID wrong", func(block *types.Block) { block.LastBlockID.PartsHeader.Total += 10 }},
    49  		{"LastCommitHash wrong", func(block *types.Block) { block.LastCommitHash = wrongHash }},
    50  		{"DataHash wrong", func(block *types.Block) { block.DataHash = wrongHash }},
    51  
    52  		{"ValidatorsHash wrong", func(block *types.Block) { block.ValidatorsHash = wrongHash }},
    53  		{"NextValidatorsHash wrong", func(block *types.Block) { block.NextValidatorsHash = wrongHash }},
    54  		{"ConsensusHash wrong", func(block *types.Block) { block.ConsensusHash = wrongHash }},
    55  		{"AppHash wrong", func(block *types.Block) { block.AppHash = wrongHash }},
    56  		{"LastResultsHash wrong", func(block *types.Block) { block.LastResultsHash = wrongHash }},
    57  
    58  		{"Proposer wrong", func(block *types.Block) { block.ProposerAddress = ed25519.GenPrivKey().PubKey().Address() }},
    59  		{"Proposer invalid", func(block *types.Block) { block.ProposerAddress = crypto.Address{} /* zero */ }},
    60  	}
    61  
    62  	// Build up state for multiple heights
    63  	for height := int64(1); height < validationTestsStopHeight; height++ {
    64  		proposerAddr := state.Validators.GetProposer().Address
    65  		/*
    66  			Invalid blocks don't pass
    67  		*/
    68  		for _, tc := range testCases {
    69  			block, _ := state.MakeBlock(height, makeTxs(height), lastCommit, proposerAddr)
    70  			tc.malleateBlock(block)
    71  			err := blockExec.ValidateBlock(state, block)
    72  			require.Error(t, err, tc.name)
    73  		}
    74  
    75  		/*
    76  			A good block passes
    77  		*/
    78  		var err error
    79  		state, _, lastCommit, err = makeAndCommitGoodBlock(state, height, lastCommit, proposerAddr, blockExec, privVals)
    80  		require.NoError(t, err, "height %d", height)
    81  	}
    82  }
    83  
    84  func TestValidateBlockCommit(t *testing.T) {
    85  	t.Parallel()
    86  
    87  	proxyApp := newTestApp()
    88  	require.NoError(t, proxyApp.Start())
    89  	defer proxyApp.Stop()
    90  
    91  	state, stateDB, privVals := makeState(1, 1)
    92  	blockExec := sm.NewBlockExecutor(stateDB, log.NewTestingLogger(t), proxyApp.Consensus(), mock.Mempool{})
    93  	lastCommit := types.NewCommit(types.BlockID{}, nil)
    94  	wrongPrecommitsCommit := types.NewCommit(types.BlockID{}, nil)
    95  	badPrivVal := types.NewMockPV()
    96  
    97  	for height := int64(1); height < validationTestsStopHeight; height++ {
    98  		proposerAddr := state.Validators.GetProposer().Address
    99  		if height > 1 {
   100  			/*
   101  				#2589: ensure state.LastValidators.VerifyCommit fails here
   102  			*/
   103  			// should be height-1 instead of height
   104  			wrongHeightVote, err := types.MakeVote(height, state.LastBlockID, state.Validators, privVals[proposerAddr.String()], chainID)
   105  			require.NoError(t, err, "height %d", height)
   106  			wrongHeightCommit := types.NewCommit(state.LastBlockID, []*types.CommitSig{wrongHeightVote.CommitSig()})
   107  			block, _ := state.MakeBlock(height, makeTxs(height), wrongHeightCommit, proposerAddr)
   108  			err = blockExec.ValidateBlock(state, block)
   109  			_, isErrInvalidCommitHeight := err.(types.InvalidCommitHeightError)
   110  			require.True(t, isErrInvalidCommitHeight, "expected InvalidCommitHeightError at height %d but got: %v", height, err)
   111  
   112  			/*
   113  				#2589: test len(block.LastCommit.Precommits) == state.LastValidators.Size()
   114  			*/
   115  			block, _ = state.MakeBlock(height, makeTxs(height), wrongPrecommitsCommit, proposerAddr)
   116  			err = blockExec.ValidateBlock(state, block)
   117  			_, isErrInvalidCommitPrecommits := err.(types.InvalidCommitPrecommitsError)
   118  			require.True(t, isErrInvalidCommitPrecommits, "expected InvalidCommitPrecommitsError at height %d but got: %v", height, err)
   119  		}
   120  
   121  		/*
   122  			A good block passes
   123  		*/
   124  		var err error
   125  		var blockID types.BlockID
   126  		state, blockID, lastCommit, err = makeAndCommitGoodBlock(state, height, lastCommit, proposerAddr, blockExec, privVals)
   127  		require.NoError(t, err, "height %d", height)
   128  
   129  		/*
   130  			wrongPrecommitsCommit is fine except for the extra bad precommit
   131  		*/
   132  		goodVote, err := types.MakeVote(height, blockID, state.Validators, privVals[proposerAddr.String()], chainID)
   133  		require.NoError(t, err, "height %d", height)
   134  		badVote := &types.Vote{
   135  			ValidatorAddress: badPrivVal.GetPubKey().Address(),
   136  			ValidatorIndex:   0,
   137  			Height:           height,
   138  			Round:            0,
   139  			Timestamp:        tmtime.Now(),
   140  			Type:             types.PrecommitType,
   141  			BlockID:          blockID,
   142  		}
   143  		err = badPrivVal.SignVote(chainID, goodVote)
   144  		require.NoError(t, err, "height %d", height)
   145  		wrongPrecommitsCommit = types.NewCommit(blockID, []*types.CommitSig{goodVote.CommitSig(), badVote.CommitSig()})
   146  	}
   147  }