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