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

     1  package types
     2  
     3  import (
     4  	"context"
     5  	"testing"
     6  	"time"
     7  
     8  	"github.com/stretchr/testify/assert"
     9  	"github.com/stretchr/testify/require"
    10  
    11  	tmmath "github.com/ari-anchor/sei-tendermint/libs/math"
    12  	tmproto "github.com/ari-anchor/sei-tendermint/proto/tendermint/types"
    13  )
    14  
    15  // Check VerifyCommit, VerifyCommitLight and VerifyCommitLightTrusting basic
    16  // verification.
    17  func TestValidatorSet_VerifyCommit_All(t *testing.T) {
    18  	var (
    19  		round  = int32(0)
    20  		height = int64(100)
    21  
    22  		blockID    = makeBlockID([]byte("blockhash"), 1000, []byte("partshash"))
    23  		chainID    = "Lalande21185"
    24  		trustLevel = tmmath.Fraction{Numerator: 2, Denominator: 3}
    25  	)
    26  
    27  	testCases := []struct {
    28  		description string
    29  		// vote chainID
    30  		chainID string
    31  		// vote blockID
    32  		blockID BlockID
    33  		valSize int
    34  
    35  		// height of the commit
    36  		height int64
    37  
    38  		// votes
    39  		blockVotes  int
    40  		nilVotes    int
    41  		absentVotes int
    42  
    43  		expErr bool
    44  	}{
    45  		{"good (batch verification)", chainID, blockID, 3, height, 3, 0, 0, false},
    46  		{"good (single verification)", chainID, blockID, 1, height, 1, 0, 0, false},
    47  
    48  		{"wrong signature (#0)", "EpsilonEridani", blockID, 2, height, 2, 0, 0, true},
    49  		{"wrong block ID", chainID, makeBlockIDRandom(), 2, height, 2, 0, 0, true},
    50  		{"wrong height", chainID, blockID, 1, height - 1, 1, 0, 0, true},
    51  
    52  		{"wrong set size: 4 vs 3", chainID, blockID, 4, height, 3, 0, 0, true},
    53  		{"wrong set size: 1 vs 2", chainID, blockID, 1, height, 2, 0, 0, true},
    54  
    55  		{"insufficient voting power: got 30, needed more than 66", chainID, blockID, 10, height, 3, 2, 5, true},
    56  		{"insufficient voting power: got 0, needed more than 6", chainID, blockID, 1, height, 0, 0, 1, true},
    57  		{"insufficient voting power: got 60, needed more than 60", chainID, blockID, 9, height, 6, 3, 0, true},
    58  	}
    59  
    60  	for _, tc := range testCases {
    61  		tc := tc
    62  		t.Run(tc.description, func(t *testing.T) {
    63  			ctx, cancel := context.WithCancel(context.Background())
    64  			defer cancel()
    65  
    66  			_, valSet, vals := randVoteSet(ctx, t, tc.height, round, tmproto.PrecommitType, tc.valSize, 10)
    67  
    68  			totalVotes := tc.blockVotes + tc.absentVotes + tc.nilVotes
    69  			sigs := make([]CommitSig, totalVotes)
    70  			vi := 0
    71  			// add absent sigs first
    72  			for i := 0; i < tc.absentVotes; i++ {
    73  				sigs[vi] = NewCommitSigAbsent()
    74  				vi++
    75  			}
    76  			for i := 0; i < tc.blockVotes+tc.nilVotes; i++ {
    77  
    78  				pubKey, err := vals[vi%len(vals)].GetPubKey(ctx)
    79  				require.NoError(t, err)
    80  				vote := &Vote{
    81  					ValidatorAddress: pubKey.Address(),
    82  					ValidatorIndex:   int32(vi),
    83  					Height:           tc.height,
    84  					Round:            round,
    85  					Type:             tmproto.PrecommitType,
    86  					BlockID:          tc.blockID,
    87  					Timestamp:        time.Now(),
    88  				}
    89  				if i >= tc.blockVotes {
    90  					vote.BlockID = BlockID{}
    91  				}
    92  
    93  				v := vote.ToProto()
    94  
    95  				require.NoError(t, vals[vi%len(vals)].SignVote(ctx, tc.chainID, v))
    96  				vote.Signature = v.Signature
    97  
    98  				sigs[vi] = vote.CommitSig()
    99  
   100  				vi++
   101  			}
   102  			commit := &Commit{
   103  				Height:     tc.height,
   104  				Round:      round,
   105  				BlockID:    tc.blockID,
   106  				Signatures: sigs,
   107  			}
   108  
   109  			err := valSet.VerifyCommit(chainID, blockID, height, commit)
   110  			if tc.expErr {
   111  				if assert.Error(t, err, "VerifyCommit") {
   112  					assert.Contains(t, err.Error(), tc.description, "VerifyCommit")
   113  				}
   114  			} else {
   115  				assert.NoError(t, err, "VerifyCommit")
   116  			}
   117  
   118  			err = valSet.VerifyCommitLight(chainID, blockID, height, commit)
   119  			if tc.expErr {
   120  				if assert.Error(t, err, "VerifyCommitLight") {
   121  					assert.Contains(t, err.Error(), tc.description, "VerifyCommitLight")
   122  				}
   123  			} else {
   124  				assert.NoError(t, err, "VerifyCommitLight")
   125  			}
   126  
   127  			// only a subsection of the tests apply to VerifyCommitLightTrusting
   128  			if totalVotes != tc.valSize || !tc.blockID.Equals(blockID) || tc.height != height {
   129  				tc.expErr = false
   130  			}
   131  			err = valSet.VerifyCommitLightTrusting(chainID, commit, trustLevel)
   132  			if tc.expErr {
   133  				if assert.Error(t, err, "VerifyCommitLightTrusting") {
   134  					assert.Contains(t, err.Error(), tc.description, "VerifyCommitLightTrusting")
   135  				}
   136  			} else {
   137  				assert.NoError(t, err, "VerifyCommitLightTrusting")
   138  			}
   139  		})
   140  	}
   141  }
   142  
   143  func TestValidatorSet_VerifyCommit_CheckAllSignatures(t *testing.T) {
   144  	var (
   145  		chainID = "test_chain_id"
   146  		h       = int64(3)
   147  		blockID = makeBlockIDRandom()
   148  	)
   149  
   150  	ctx, cancel := context.WithCancel(context.Background())
   151  	defer cancel()
   152  
   153  	voteSet, valSet, vals := randVoteSet(ctx, t, h, 0, tmproto.PrecommitType, 4, 10)
   154  	extCommit, err := makeExtCommit(ctx, blockID, h, 0, voteSet, vals, time.Now())
   155  	require.NoError(t, err)
   156  	commit := extCommit.ToCommit()
   157  
   158  	require.NoError(t, valSet.VerifyCommit(chainID, blockID, h, commit))
   159  
   160  	// malleate 4th signature
   161  	vote := voteSet.GetByIndex(3)
   162  	v := vote.ToProto()
   163  	err = vals[3].SignVote(ctx, "CentaurusA", v)
   164  	require.NoError(t, err)
   165  	vote.Signature = v.Signature
   166  	commit.Signatures[3] = vote.CommitSig()
   167  
   168  	err = valSet.VerifyCommit(chainID, blockID, h, commit)
   169  	if assert.Error(t, err) {
   170  		assert.Contains(t, err.Error(), "wrong signature (#3)")
   171  	}
   172  }
   173  
   174  func TestValidatorSet_VerifyCommitLight_ReturnsAsSoonAsMajorityOfVotingPowerSigned(t *testing.T) {
   175  	var (
   176  		chainID = "test_chain_id"
   177  		h       = int64(3)
   178  		blockID = makeBlockIDRandom()
   179  	)
   180  
   181  	ctx, cancel := context.WithCancel(context.Background())
   182  	defer cancel()
   183  
   184  	voteSet, valSet, vals := randVoteSet(ctx, t, h, 0, tmproto.PrecommitType, 4, 10)
   185  	extCommit, err := makeExtCommit(ctx, blockID, h, 0, voteSet, vals, time.Now())
   186  	require.NoError(t, err)
   187  	commit := extCommit.ToCommit()
   188  
   189  	require.NoError(t, valSet.VerifyCommit(chainID, blockID, h, commit))
   190  
   191  	// malleate 4th signature (3 signatures are enough for 2/3+)
   192  	vote := voteSet.GetByIndex(3)
   193  	v := vote.ToProto()
   194  	err = vals[3].SignVote(ctx, "CentaurusA", v)
   195  	require.NoError(t, err)
   196  	vote.Signature = v.Signature
   197  	commit.Signatures[3] = vote.CommitSig()
   198  
   199  	err = valSet.VerifyCommitLight(chainID, blockID, h, commit)
   200  	assert.NoError(t, err)
   201  }
   202  
   203  func TestValidatorSet_VerifyCommitLightTrusting_ReturnsAsSoonAsTrustLevelOfVotingPowerSigned(t *testing.T) {
   204  	var (
   205  		chainID = "test_chain_id"
   206  		h       = int64(3)
   207  		blockID = makeBlockIDRandom()
   208  	)
   209  	ctx, cancel := context.WithCancel(context.Background())
   210  	defer cancel()
   211  
   212  	voteSet, valSet, vals := randVoteSet(ctx, t, h, 0, tmproto.PrecommitType, 4, 10)
   213  	extCommit, err := makeExtCommit(ctx, blockID, h, 0, voteSet, vals, time.Now())
   214  	require.NoError(t, err)
   215  	commit := extCommit.ToCommit()
   216  
   217  	require.NoError(t, valSet.VerifyCommit(chainID, blockID, h, commit))
   218  
   219  	// malleate 3rd signature (2 signatures are enough for 1/3+ trust level)
   220  	vote := voteSet.GetByIndex(2)
   221  	v := vote.ToProto()
   222  	err = vals[2].SignVote(ctx, "CentaurusA", v)
   223  	require.NoError(t, err)
   224  	vote.Signature = v.Signature
   225  	commit.Signatures[2] = vote.CommitSig()
   226  
   227  	err = valSet.VerifyCommitLightTrusting(chainID, commit, tmmath.Fraction{Numerator: 1, Denominator: 3})
   228  	assert.NoError(t, err)
   229  }
   230  
   231  func TestValidatorSet_VerifyCommitLightTrusting(t *testing.T) {
   232  	ctx, cancel := context.WithCancel(context.Background())
   233  	defer cancel()
   234  
   235  	var (
   236  		blockID                       = makeBlockIDRandom()
   237  		voteSet, originalValset, vals = randVoteSet(ctx, t, 1, 1, tmproto.PrecommitType, 6, 1)
   238  		extCommit, err                = makeExtCommit(ctx, blockID, 1, 1, voteSet, vals, time.Now())
   239  		newValSet, _                  = randValidatorPrivValSet(ctx, t, 2, 1)
   240  	)
   241  	require.NoError(t, err)
   242  	commit := extCommit.ToCommit()
   243  
   244  	testCases := []struct {
   245  		valSet *ValidatorSet
   246  		err    bool
   247  	}{
   248  		// good
   249  		0: {
   250  			valSet: originalValset,
   251  			err:    false,
   252  		},
   253  		// bad - no overlap between validator sets
   254  		1: {
   255  			valSet: newValSet,
   256  			err:    true,
   257  		},
   258  		// good - first two are different but the rest of the same -> >1/3
   259  		2: {
   260  			valSet: NewValidatorSet(append(newValSet.Validators, originalValset.Validators...)),
   261  			err:    false,
   262  		},
   263  	}
   264  
   265  	for _, tc := range testCases {
   266  		err = tc.valSet.VerifyCommitLightTrusting("test_chain_id", commit,
   267  			tmmath.Fraction{Numerator: 1, Denominator: 3})
   268  		if tc.err {
   269  			assert.Error(t, err)
   270  		} else {
   271  			assert.NoError(t, err)
   272  		}
   273  	}
   274  }
   275  
   276  func TestValidatorSet_VerifyCommitLightTrustingErrorsOnOverflow(t *testing.T) {
   277  	ctx, cancel := context.WithCancel(context.Background())
   278  	defer cancel()
   279  
   280  	var (
   281  		blockID               = makeBlockIDRandom()
   282  		voteSet, valSet, vals = randVoteSet(ctx, t, 1, 1, tmproto.PrecommitType, 1, MaxTotalVotingPower)
   283  		extCommit, err        = makeExtCommit(ctx, blockID, 1, 1, voteSet, vals, time.Now())
   284  	)
   285  	require.NoError(t, err)
   286  
   287  	err = valSet.VerifyCommitLightTrusting("test_chain_id", extCommit.ToCommit(),
   288  		tmmath.Fraction{Numerator: 25, Denominator: 55})
   289  	if assert.Error(t, err) {
   290  		assert.Contains(t, err.Error(), "int64 overflow")
   291  	}
   292  }