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 }