Skip to content

Commit 4fb26e5

Browse files
mergify[bot]sainoe
andauthored
fix!: add check for the height of evidence (backport #2007) (#2011)
fix!: add check for the height of evidence (#2007) * init commit * added check, setting, and deleting of the equivocation min height * update changelog entry * remove unwwanted changelog entry --------- Co-authored-by: insumity <karolos@informal.systems> (cherry picked from commit de9db96) Co-authored-by: Simon Noetzlin <simon.ntz@gmail.com>
1 parent 2736dca commit 4fb26e5

File tree

6 files changed

+83
-18
lines changed

6 files changed

+83
-18
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
- Add missing check for the minimum height of evidence in the consumer double-vote handler.
2+
[#2007](https://github.com/cosmos/interchain-security/pull/2007)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
- Add missing check for the minimum height of evidence in the consumer double-vote handler.
2+
[#2007](https://github.com/cosmos/interchain-security/pull/2007)

tests/integration/double_vote.go

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,9 @@ func (s *CCVTestSuite) TestHandleConsumerDoubleVoting() {
8686
s.consumerChain.ChainID,
8787
)
8888

89-
// create a vote using the consumer validator key
90-
// with block height that is smaller than the equivocation evidence min height
91-
consuVoteOld := testutil.MakeAndSignVote(
89+
// create two votes using the consumer validator key that both have
90+
// the same block height that is smaller than the equivocation evidence min height
91+
consuVoteOld1 := testutil.MakeAndSignVote(
9292
blockID1,
9393
int64(equivocationEvidenceMinHeight-1),
9494
s.consumerCtx().BlockTime(),
@@ -97,6 +97,15 @@ func (s *CCVTestSuite) TestHandleConsumerDoubleVoting() {
9797
s.consumerChain.ChainID,
9898
)
9999

100+
consuVoteOld2 := testutil.MakeAndSignVote(
101+
blockID2,
102+
int64(equivocationEvidenceMinHeight-1),
103+
s.consumerCtx().BlockTime(),
104+
consuValSet,
105+
consuSigner,
106+
s.consumerChain.ChainID,
107+
)
108+
100109
testCases := []struct {
101110
name string
102111
ev *tmtypes.DuplicateVoteEvidence
@@ -120,8 +129,8 @@ func (s *CCVTestSuite) TestHandleConsumerDoubleVoting() {
120129
{
121130
"evidence is older than equivocation evidence min height - shouldn't pass",
122131
&tmtypes.DuplicateVoteEvidence{
123-
VoteA: consuVoteOld,
124-
VoteB: consuBadVote,
132+
VoteA: consuVoteOld1,
133+
VoteB: consuVoteOld2,
125134
ValidatorPower: consuVal.VotingPower,
126135
TotalVotingPower: consuVal.VotingPower,
127136
Timestamp: s.consumerCtx().BlockTime(),
@@ -134,7 +143,7 @@ func (s *CCVTestSuite) TestHandleConsumerDoubleVoting() {
134143
"the votes in the evidence are for different height - shouldn't pass",
135144
&tmtypes.DuplicateVoteEvidence{
136145
VoteA: consuVote,
137-
VoteB: consuVoteOld,
146+
VoteB: consuVoteOld1,
138147
ValidatorPower: consuVal.VotingPower,
139148
TotalVotingPower: consuVal.VotingPower,
140149
Timestamp: s.consumerCtx().BlockTime(),

tests/integration/misbehaviour.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -509,7 +509,16 @@ func (s *CCVTestSuite) TestCheckMisbehaviour() {
509509
clientTMValset,
510510
altSigners,
511511
),
512-
Header2: clientHeader,
512+
Header2: s.consumerChain.CreateTMClientHeader(
513+
s.consumerChain.ChainID,
514+
int64(equivocationEvidenceMinHeight-1),
515+
clientHeight,
516+
headerTs,
517+
clientTMValset,
518+
clientTMValset,
519+
clientTMValset,
520+
clientSigners,
521+
),
513522
},
514523
false,
515524
},

x/ccv/provider/keeper/consumer_equivocation.go

Lines changed: 50 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"bytes"
55
"encoding/binary"
66
"fmt"
7+
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
78

89
ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types"
910
ibctmtypes "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint"
@@ -35,6 +36,27 @@ func (k Keeper) HandleConsumerDoubleVoting(
3536
chainID string,
3637
pubkey cryptotypes.PubKey,
3738
) error {
39+
// check that the evidence is for an ICS consumer chain
40+
if _, found := k.GetConsumerClientId(ctx, chainID); !found {
41+
return errorsmod.Wrapf(
42+
ccvtypes.ErrInvalidDoubleVotingEvidence,
43+
"cannot find consumer chain %s",
44+
chainID,
45+
)
46+
}
47+
48+
// check that the evidence is not too old
49+
minHeight := k.GetEquivocationEvidenceMinHeight(ctx, chainID)
50+
if uint64(evidence.VoteA.Height) < minHeight {
51+
return errorsmod.Wrapf(
52+
ccvtypes.ErrInvalidDoubleVotingEvidence,
53+
"evidence for consumer chain %s is too old - evidence height (%d), min (%d)",
54+
chainID,
55+
evidence.VoteA.Height,
56+
minHeight,
57+
)
58+
}
59+
3860
// verifies the double voting evidence using the consumer chain public key
3961
if err := k.VerifyDoubleVotingEvidence(*evidence, chainID, pubkey); err != nil {
4062
return err
@@ -269,34 +291,51 @@ func headerToLightBlock(h ibctmtypes.Header) (*tmtypes.LightBlock, error) {
269291
}
270292

271293
// CheckMisbehaviour checks that headers in the given misbehaviour forms
272-
// a valid light client attack on a light client that tracks an ICS consumer chain
294+
// a valid light client attack from an ICS consumer chain and that the light client isn't expired
273295
func (k Keeper) CheckMisbehaviour(ctx sdk.Context, misbehaviour ibctmtypes.Misbehaviour) error {
296+
consumerChainID := misbehaviour.Header1.Header.ChainID
297+
274298
// check that the misbehaviour is for an ICS consumer chain
275-
clientId, found := k.GetConsumerClientId(ctx, misbehaviour.Header1.Header.ChainID)
299+
clientId, found := k.GetConsumerClientId(ctx, consumerChainID)
276300
if !found {
277-
return fmt.Errorf("incorrect misbehaviour with conflicting headers from a non-existent consumer chain: %s", misbehaviour.Header1.Header.ChainID)
301+
return fmt.Errorf("incorrect misbehaviour with conflicting headers from a non-existent consumer chain: %s", consumerChainID)
278302
} else if misbehaviour.ClientId != clientId {
279303
return fmt.Errorf("incorrect misbehaviour: expected client ID for consumer chain %s is %s got %s",
280-
misbehaviour.Header1.Header.ChainID,
304+
consumerChainID,
281305
clientId,
282306
misbehaviour.ClientId,
283307
)
284308
}
285309

310+
// Check that the headers are at the same height to ensure that
311+
// the misbehaviour is for a light client attack and not a time violation,
312+
// see ibc-go/modules/light-clients/07-tendermint/types/misbehaviour_handle.go
313+
if !misbehaviour.Header1.GetHeight().EQ(misbehaviour.Header2.GetHeight()) {
314+
return sdkerrors.Wrap(ibcclienttypes.ErrInvalidMisbehaviour, "headers are not at same height")
315+
}
316+
317+
// Check that the evidence is not too old
318+
minHeight := k.GetEquivocationEvidenceMinHeight(ctx, consumerChainID)
319+
evidenceHeight := misbehaviour.Header1.GetHeight().GetRevisionHeight()
320+
// Note that the revision number is not relevant for checking the age of evidence
321+
// as it's already part of the chain ID and the minimum height is mapped to chain IDs
322+
if evidenceHeight < minHeight {
323+
return errorsmod.Wrapf(
324+
ccvtypes.ErrInvalidDoubleVotingEvidence,
325+
"evidence for consumer chain %s is too old - evidence height (%d), min (%d)",
326+
consumerChainID,
327+
evidenceHeight,
328+
minHeight,
329+
)
330+
}
331+
286332
clientState, found := k.clientKeeper.GetClientState(ctx, clientId)
287333
if !found {
288334
return errorsmod.Wrapf(ibcclienttypes.ErrClientNotFound, "cannot find client state for client with ID %s", clientId)
289335
}
290336

291337
clientStore := k.clientKeeper.ClientStore(ctx, clientId)
292338

293-
// Check that the headers are at the same height to ensure that
294-
// the misbehaviour is for a light client attack and not a time violation,
295-
// see CheckForMisbehaviour in ibc-go/blob/v7.3.0/modules/light-clients/07-tendermint/misbehaviour_handle.go#L73
296-
if !misbehaviour.Header1.GetHeight().EQ(misbehaviour.Header2.GetHeight()) {
297-
return errorsmod.Wrap(ibcclienttypes.ErrInvalidMisbehaviour, "headers are not at same height")
298-
}
299-
300339
// CheckForMisbehaviour verifies that the headers have different blockID hashes
301340
ok := clientState.CheckForMisbehaviour(ctx, k.cdc, clientStore, &misbehaviour)
302341
if !ok {

x/ccv/provider/keeper/proposal.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ func (k Keeper) CreateConsumerClient(ctx sdk.Context, prop *types.ConsumerAdditi
5858
fmt.Sprintf("cannot create client for existent consumer chain: %s", chainID))
5959
}
6060

61+
// Set minimum height for equivocation evidence from this consumer chain
62+
k.SetEquivocationEvidenceMinHeight(ctx, chainID, prop.InitialHeight.RevisionHeight)
63+
6164
// Consumers start out with the unbonding period from the consumer addition prop
6265
consumerUnbondingPeriod := prop.UnbondingPeriod
6366

@@ -219,6 +222,7 @@ func (k Keeper) StopConsumerChain(ctx sdk.Context, chainID string, closeChan boo
219222
// Note: this call panics if the key assignment state is invalid
220223
k.DeleteKeyAssignments(ctx, chainID)
221224
k.DeleteMinimumPowerInTopN(ctx, chainID)
225+
k.DeleteEquivocationEvidenceMinHeight(ctx, chainID)
222226

223227
// close channel and delete the mappings between chain ID and channel ID
224228
if channelID, found := k.GetChainToChannel(ctx, chainID); found {

0 commit comments

Comments
 (0)