github.com/project-88388/tendermint-v0.34.14-terra.2@v1.0.0/types/evidence_test.go (about) 1 package types 2 3 import ( 4 "math" 5 "testing" 6 "time" 7 8 "github.com/stretchr/testify/assert" 9 "github.com/stretchr/testify/require" 10 11 "github.com/tendermint/tendermint/crypto" 12 "github.com/tendermint/tendermint/crypto/tmhash" 13 tmrand "github.com/tendermint/tendermint/libs/rand" 14 tmproto "github.com/tendermint/tendermint/proto/tendermint/types" 15 tmversion "github.com/tendermint/tendermint/proto/tendermint/version" 16 "github.com/tendermint/tendermint/version" 17 ) 18 19 var defaultVoteTime = time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC) 20 21 func TestEvidenceList(t *testing.T) { 22 ev := randomDuplicateVoteEvidence(t) 23 evl := EvidenceList([]Evidence{ev}) 24 25 assert.NotNil(t, evl.Hash()) 26 assert.True(t, evl.Has(ev)) 27 assert.False(t, evl.Has(&DuplicateVoteEvidence{})) 28 } 29 30 func randomDuplicateVoteEvidence(t *testing.T) *DuplicateVoteEvidence { 31 val := NewMockPV() 32 blockID := makeBlockID([]byte("blockhash"), 1000, []byte("partshash")) 33 blockID2 := makeBlockID([]byte("blockhash2"), 1000, []byte("partshash")) 34 const chainID = "mychain" 35 return &DuplicateVoteEvidence{ 36 VoteA: makeVote(t, val, chainID, 0, 10, 2, 1, blockID, defaultVoteTime), 37 VoteB: makeVote(t, val, chainID, 0, 10, 2, 1, blockID2, defaultVoteTime.Add(1*time.Minute)), 38 TotalVotingPower: 30, 39 ValidatorPower: 10, 40 Timestamp: defaultVoteTime, 41 } 42 } 43 44 func TestDuplicateVoteEvidence(t *testing.T) { 45 const height = int64(13) 46 ev := NewMockDuplicateVoteEvidence(height, time.Now(), "mock-chain-id") 47 assert.Equal(t, ev.Hash(), tmhash.Sum(ev.Bytes())) 48 assert.NotNil(t, ev.String()) 49 assert.Equal(t, ev.Height(), height) 50 } 51 52 func TestDuplicateVoteEvidenceValidation(t *testing.T) { 53 val := NewMockPV() 54 blockID := makeBlockID(tmhash.Sum([]byte("blockhash")), math.MaxInt32, tmhash.Sum([]byte("partshash"))) 55 blockID2 := makeBlockID(tmhash.Sum([]byte("blockhash2")), math.MaxInt32, tmhash.Sum([]byte("partshash"))) 56 const chainID = "mychain" 57 58 testCases := []struct { 59 testName string 60 malleateEvidence func(*DuplicateVoteEvidence) 61 expectErr bool 62 }{ 63 {"Good DuplicateVoteEvidence", func(ev *DuplicateVoteEvidence) {}, false}, 64 {"Nil vote A", func(ev *DuplicateVoteEvidence) { ev.VoteA = nil }, true}, 65 {"Nil vote B", func(ev *DuplicateVoteEvidence) { ev.VoteB = nil }, true}, 66 {"Nil votes", func(ev *DuplicateVoteEvidence) { 67 ev.VoteA = nil 68 ev.VoteB = nil 69 }, true}, 70 {"Invalid vote type", func(ev *DuplicateVoteEvidence) { 71 ev.VoteA = makeVote(t, val, chainID, math.MaxInt32, math.MaxInt64, math.MaxInt32, 0, blockID2, defaultVoteTime) 72 }, true}, 73 {"Invalid vote order", func(ev *DuplicateVoteEvidence) { 74 swap := ev.VoteA.Copy() 75 ev.VoteA = ev.VoteB.Copy() 76 ev.VoteB = swap 77 }, true}, 78 } 79 for _, tc := range testCases { 80 tc := tc 81 t.Run(tc.testName, func(t *testing.T) { 82 vote1 := makeVote(t, val, chainID, math.MaxInt32, math.MaxInt64, math.MaxInt32, 0x02, blockID, defaultVoteTime) 83 vote2 := makeVote(t, val, chainID, math.MaxInt32, math.MaxInt64, math.MaxInt32, 0x02, blockID2, defaultVoteTime) 84 valSet := NewValidatorSet([]*Validator{val.ExtractIntoValidator(10)}) 85 ev := NewDuplicateVoteEvidence(vote1, vote2, defaultVoteTime, valSet) 86 tc.malleateEvidence(ev) 87 assert.Equal(t, tc.expectErr, ev.ValidateBasic() != nil, "Validate Basic had an unexpected result") 88 }) 89 } 90 } 91 92 func TestLightClientAttackEvidenceBasic(t *testing.T) { 93 height := int64(5) 94 commonHeight := height - 1 95 nValidators := 10 96 voteSet, valSet, privVals := randVoteSet(height, 1, tmproto.PrecommitType, nValidators, 1) 97 header := makeHeaderRandom() 98 header.Height = height 99 blockID := makeBlockID(tmhash.Sum([]byte("blockhash")), math.MaxInt32, tmhash.Sum([]byte("partshash"))) 100 commit, err := MakeCommit(blockID, height, 1, voteSet, privVals, defaultVoteTime) 101 require.NoError(t, err) 102 lcae := &LightClientAttackEvidence{ 103 ConflictingBlock: &LightBlock{ 104 SignedHeader: &SignedHeader{ 105 Header: header, 106 Commit: commit, 107 }, 108 ValidatorSet: valSet, 109 }, 110 CommonHeight: commonHeight, 111 TotalVotingPower: valSet.TotalVotingPower(), 112 Timestamp: header.Time, 113 ByzantineValidators: valSet.Validators[:nValidators/2], 114 } 115 assert.NotNil(t, lcae.String()) 116 assert.NotNil(t, lcae.Hash()) 117 assert.Equal(t, lcae.Height(), commonHeight) // Height should be the common Height 118 assert.NotNil(t, lcae.Bytes()) 119 120 // maleate evidence to test hash uniqueness 121 testCases := []struct { 122 testName string 123 malleateEvidence func(*LightClientAttackEvidence) 124 }{ 125 {"Different header", func(ev *LightClientAttackEvidence) { ev.ConflictingBlock.Header = makeHeaderRandom() }}, 126 {"Different common height", func(ev *LightClientAttackEvidence) { 127 ev.CommonHeight = height + 1 128 }}, 129 } 130 131 for _, tc := range testCases { 132 lcae := &LightClientAttackEvidence{ 133 ConflictingBlock: &LightBlock{ 134 SignedHeader: &SignedHeader{ 135 Header: header, 136 Commit: commit, 137 }, 138 ValidatorSet: valSet, 139 }, 140 CommonHeight: commonHeight, 141 TotalVotingPower: valSet.TotalVotingPower(), 142 Timestamp: header.Time, 143 ByzantineValidators: valSet.Validators[:nValidators/2], 144 } 145 hash := lcae.Hash() 146 tc.malleateEvidence(lcae) 147 assert.NotEqual(t, hash, lcae.Hash(), tc.testName) 148 } 149 } 150 151 func TestLightClientAttackEvidenceValidation(t *testing.T) { 152 height := int64(5) 153 commonHeight := height - 1 154 nValidators := 10 155 voteSet, valSet, privVals := randVoteSet(height, 1, tmproto.PrecommitType, nValidators, 1) 156 header := makeHeaderRandom() 157 header.Height = height 158 header.ValidatorsHash = valSet.Hash() 159 blockID := makeBlockID(header.Hash(), math.MaxInt32, tmhash.Sum([]byte("partshash"))) 160 commit, err := MakeCommit(blockID, height, 1, voteSet, privVals, time.Now()) 161 require.NoError(t, err) 162 lcae := &LightClientAttackEvidence{ 163 ConflictingBlock: &LightBlock{ 164 SignedHeader: &SignedHeader{ 165 Header: header, 166 Commit: commit, 167 }, 168 ValidatorSet: valSet, 169 }, 170 CommonHeight: commonHeight, 171 TotalVotingPower: valSet.TotalVotingPower(), 172 Timestamp: header.Time, 173 ByzantineValidators: valSet.Validators[:nValidators/2], 174 } 175 assert.NoError(t, lcae.ValidateBasic()) 176 177 testCases := []struct { 178 testName string 179 malleateEvidence func(*LightClientAttackEvidence) 180 expectErr bool 181 }{ 182 {"Good LightClientAttackEvidence", func(ev *LightClientAttackEvidence) {}, false}, 183 {"Negative height", func(ev *LightClientAttackEvidence) { ev.CommonHeight = -10 }, true}, 184 {"Height is greater than divergent block", func(ev *LightClientAttackEvidence) { 185 ev.CommonHeight = height + 1 186 }, true}, 187 {"Height is equal to the divergent block", func(ev *LightClientAttackEvidence) { 188 ev.CommonHeight = height 189 }, false}, 190 {"Nil conflicting header", func(ev *LightClientAttackEvidence) { ev.ConflictingBlock.Header = nil }, true}, 191 {"Nil conflicting blocl", func(ev *LightClientAttackEvidence) { ev.ConflictingBlock = nil }, true}, 192 {"Nil validator set", func(ev *LightClientAttackEvidence) { 193 ev.ConflictingBlock.ValidatorSet = &ValidatorSet{} 194 }, true}, 195 {"Negative total voting power", func(ev *LightClientAttackEvidence) { 196 ev.TotalVotingPower = -1 197 }, true}, 198 } 199 for _, tc := range testCases { 200 tc := tc 201 t.Run(tc.testName, func(t *testing.T) { 202 lcae := &LightClientAttackEvidence{ 203 ConflictingBlock: &LightBlock{ 204 SignedHeader: &SignedHeader{ 205 Header: header, 206 Commit: commit, 207 }, 208 ValidatorSet: valSet, 209 }, 210 CommonHeight: commonHeight, 211 TotalVotingPower: valSet.TotalVotingPower(), 212 Timestamp: header.Time, 213 ByzantineValidators: valSet.Validators[:nValidators/2], 214 } 215 tc.malleateEvidence(lcae) 216 if tc.expectErr { 217 assert.Error(t, lcae.ValidateBasic(), tc.testName) 218 } else { 219 assert.NoError(t, lcae.ValidateBasic(), tc.testName) 220 } 221 }) 222 } 223 224 } 225 226 func TestMockEvidenceValidateBasic(t *testing.T) { 227 goodEvidence := NewMockDuplicateVoteEvidence(int64(1), time.Now(), "mock-chain-id") 228 assert.Nil(t, goodEvidence.ValidateBasic()) 229 } 230 231 func makeVote( 232 t *testing.T, val PrivValidator, chainID string, valIndex int32, height int64, round int32, step int, blockID BlockID, 233 time time.Time) *Vote { 234 pubKey, err := val.GetPubKey() 235 require.NoError(t, err) 236 v := &Vote{ 237 ValidatorAddress: pubKey.Address(), 238 ValidatorIndex: valIndex, 239 Height: height, 240 Round: round, 241 Type: tmproto.SignedMsgType(step), 242 BlockID: blockID, 243 Timestamp: time, 244 } 245 246 vpb := v.ToProto() 247 err = val.SignVote(chainID, vpb) 248 if err != nil { 249 panic(err) 250 } 251 v.Signature = vpb.Signature 252 return v 253 } 254 255 func makeHeaderRandom() *Header { 256 return &Header{ 257 Version: tmversion.Consensus{Block: version.BlockProtocol, App: 1}, 258 ChainID: tmrand.Str(12), 259 Height: int64(tmrand.Uint16()) + 1, 260 Time: time.Now(), 261 LastBlockID: makeBlockIDRandom(), 262 LastCommitHash: crypto.CRandBytes(tmhash.Size), 263 DataHash: crypto.CRandBytes(tmhash.Size), 264 ValidatorsHash: crypto.CRandBytes(tmhash.Size), 265 NextValidatorsHash: crypto.CRandBytes(tmhash.Size), 266 ConsensusHash: crypto.CRandBytes(tmhash.Size), 267 AppHash: crypto.CRandBytes(tmhash.Size), 268 LastResultsHash: crypto.CRandBytes(tmhash.Size), 269 EvidenceHash: crypto.CRandBytes(tmhash.Size), 270 ProposerAddress: crypto.CRandBytes(crypto.AddressSize), 271 } 272 } 273 274 func TestEvidenceProto(t *testing.T) { 275 // -------- Votes -------- 276 val := NewMockPV() 277 blockID := makeBlockID(tmhash.Sum([]byte("blockhash")), math.MaxInt32, tmhash.Sum([]byte("partshash"))) 278 blockID2 := makeBlockID(tmhash.Sum([]byte("blockhash2")), math.MaxInt32, tmhash.Sum([]byte("partshash"))) 279 const chainID = "mychain" 280 v := makeVote(t, val, chainID, math.MaxInt32, math.MaxInt64, 1, 0x01, blockID, defaultVoteTime) 281 v2 := makeVote(t, val, chainID, math.MaxInt32, math.MaxInt64, 2, 0x01, blockID2, defaultVoteTime) 282 283 // -------- SignedHeaders -------- 284 const height int64 = 37 285 286 var ( 287 header1 = makeHeaderRandom() 288 header2 = makeHeaderRandom() 289 ) 290 291 header1.Height = height 292 header1.LastBlockID = blockID 293 header1.ChainID = chainID 294 295 header2.Height = height 296 header2.LastBlockID = blockID 297 header2.ChainID = chainID 298 299 tests := []struct { 300 testName string 301 evidence Evidence 302 toProtoErr bool 303 fromProtoErr bool 304 }{ 305 {"nil fail", nil, true, true}, 306 {"DuplicateVoteEvidence empty fail", &DuplicateVoteEvidence{}, false, true}, 307 {"DuplicateVoteEvidence nil voteB", &DuplicateVoteEvidence{VoteA: v, VoteB: nil}, false, true}, 308 {"DuplicateVoteEvidence nil voteA", &DuplicateVoteEvidence{VoteA: nil, VoteB: v}, false, true}, 309 {"DuplicateVoteEvidence success", &DuplicateVoteEvidence{VoteA: v2, VoteB: v}, false, false}, 310 } 311 for _, tt := range tests { 312 tt := tt 313 t.Run(tt.testName, func(t *testing.T) { 314 pb, err := EvidenceToProto(tt.evidence) 315 if tt.toProtoErr { 316 assert.Error(t, err, tt.testName) 317 return 318 } 319 assert.NoError(t, err, tt.testName) 320 321 evi, err := EvidenceFromProto(pb) 322 if tt.fromProtoErr { 323 assert.Error(t, err, tt.testName) 324 return 325 } 326 require.Equal(t, tt.evidence, evi, tt.testName) 327 }) 328 } 329 }