Skip to content

Commit

Permalink
filter consumer evidence based on min height
Browse files Browse the repository at this point in the history
  • Loading branch information
mpoke committed Nov 17, 2023
1 parent 7f12297 commit 1466116
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 10 deletions.
79 changes: 69 additions & 10 deletions x/ccv/provider/keeper/consumer_equivocation.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package keeper

import (
"bytes"
"encoding/binary"
"fmt"

errorsmod "cosmossdk.io/errors"
Expand Down Expand Up @@ -39,6 +40,18 @@ func (k Keeper) HandleConsumerDoubleVoting(
)
}

// check that the evidence is not too old
minHeight := k.GetEquivocationEvidenceMinHeight(ctx, chainID)
if uint64(evidence.VoteA.Height) < minHeight {
return errorsmod.Wrapf(
ccvtypes.ErrInvalidDoubleVotingEvidence,
"evidence for consumer chain %s is too old - evidence height (%d), min (%d)",
chainID,
evidence.VoteA.Height,
minHeight,
)
}

// verifies the double voting evidence using the consumer chain public key
if err := k.VerifyDoubleVotingEvidence(*evidence, chainID, pubkey); err != nil {
return err
Expand Down Expand Up @@ -276,32 +289,49 @@ func headerToLightBlock(h ibctmtypes.Header) (*tmtypes.LightBlock, error) {
// CheckMisbehaviour checks that headers in the given misbehaviour forms
// a valid light client attack from an ICS consumer chain and that the light client isn't expired
func (k Keeper) CheckMisbehaviour(ctx sdk.Context, misbehaviour ibctmtypes.Misbehaviour) error {
consumerChainID := misbehaviour.Header1.Header.ChainID

// check that the misbehaviour is for an ICS consumer chain
clientId, found := k.GetConsumerClientId(ctx, misbehaviour.Header1.Header.ChainID)
clientId, found := k.GetConsumerClientId(ctx, consumerChainID)
if !found {
return fmt.Errorf("incorrect misbehaviour with conflicting headers from a non-existent consumer chain: %s", misbehaviour.Header1.Header.ChainID)
return fmt.Errorf("incorrect misbehaviour with conflicting headers from a non-existent consumer chain: %s", consumerChainID)
} else if misbehaviour.ClientId != clientId {
return fmt.Errorf("incorrect misbehaviour: expected client ID for consumer chain %s is %s got %s",
misbehaviour.Header1.Header.ChainID,
consumerChainID,
clientId,
misbehaviour.ClientId,
)
}

clientState, found := k.clientKeeper.GetClientState(ctx, clientId)
if !found {
return sdkerrors.Wrapf(ibcclienttypes.ErrClientNotFound, "cannot check misbehaviour for client with ID %s", misbehaviour.GetClientID())
}

clientStore := k.clientKeeper.ClientStore(ctx, misbehaviour.GetClientID())

// Check that the headers are at the same height to ensure that
// the misbehaviour is for a light client attack and not a time violation,
// see ibc-go/modules/light-clients/07-tendermint/types/misbehaviour_handle.go
if !misbehaviour.Header1.GetHeight().EQ(misbehaviour.Header2.GetHeight()) {
return sdkerrors.Wrap(ibcclienttypes.ErrInvalidMisbehaviour, "headers are not at same height")
}

// Check that the evidence is not too old
minHeight := k.GetEquivocationEvidenceMinHeight(ctx, consumerChainID)
evidenceHeight := misbehaviour.Header1.GetHeight().GetRevisionHeight()
// Note that the revision number is not relevant for checking the age of evidence
// as it's already part of the chain ID and the minimum height is mapped to chain IDs
if evidenceHeight < minHeight {
return errorsmod.Wrapf(
ccvtypes.ErrInvalidDoubleVotingEvidence,
"evidence for consumer chain %s is too old - evidence height (%d), min (%d)",
consumerChainID,
evidenceHeight,
minHeight,
)
}

clientState, found := k.clientKeeper.GetClientState(ctx, clientId)
if !found {
return sdkerrors.Wrapf(ibcclienttypes.ErrClientNotFound, "cannot check misbehaviour for client with ID %s", misbehaviour.GetClientID())
}

clientStore := k.clientKeeper.ClientStore(ctx, misbehaviour.GetClientID())

// CheckMisbehaviourAndUpdateState verifies the misbehaviour against the trusted consensus states
// but does NOT update the light client state.
// Note that the IBC CheckMisbehaviourAndUpdateState method returns an error if the trusted consensus states are expired,
Expand Down Expand Up @@ -444,3 +474,32 @@ func (k Keeper) SlashValidator(ctx sdk.Context, providerAddr providertypes.Provi
//
// CRUD section
//

// SetEquivocationEvidenceMinHeight sets the the minimum height
// of a valid consumer equivocation evidence for a given consumer chain ID
func (k Keeper) SetEquivocationEvidenceMinHeight(ctx sdk.Context, chainID string, height uint64) {
store := ctx.KVStore(k.storeKey)
heightBytes := make([]byte, 8)
binary.BigEndian.PutUint64(heightBytes, height)

store.Set(providertypes.EquivocationEvidenceMinHeightKey(chainID), heightBytes)
}

// GetEquivocationEvidenceMinHeight returns the the minimum height
// of a valid consumer equivocation evidence for a given consumer chain ID
func (k Keeper) GetEquivocationEvidenceMinHeight(ctx sdk.Context, chainID string) uint64 {
store := ctx.KVStore(k.storeKey)
bz := store.Get(providertypes.EquivocationEvidenceMinHeightKey(chainID))
if bz == nil {
return 0
}

return binary.BigEndian.Uint64(bz)
}

// DeleteEquivocationEvidenceMinHeight deletes the the minimum height
// of a valid consumer equivocation evidence for a given consumer chain ID
func (k Keeper) DeleteEquivocationEvidenceMinHeight(ctx sdk.Context, chainID string) {
store := ctx.KVStore(k.storeKey)
store.Delete(providertypes.EquivocationEvidenceMinHeightKey(chainID))
}
10 changes: 10 additions & 0 deletions x/ccv/provider/types/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,10 @@ const (
// handled in the current block
VSCMaturedHandledThisBlockBytePrefix

// EquivocationEvidenceMinHeightBytePrefix is the byte prefix storing the mapping from consumer chain IDs
// to the minimum height of a valid consumer equivocation evidence
EquivocationEvidenceMinHeightBytePrefix

// NOTE: DO NOT ADD NEW BYTE PREFIXES HERE WITHOUT ADDING THEM TO getAllKeyPrefixes() IN keys_test.go
)

Expand Down Expand Up @@ -377,6 +381,12 @@ func ConsumerRewardDenomsKey(denom string) []byte {
return append([]byte{ConsumerRewardDenomsBytePrefix}, []byte(denom)...)
}

// EquivocationEvidenceMinHeightKey returns the key storing the minimum height
// of a valid consumer equivocation evidence for a given consumer chain ID
func EquivocationEvidenceMinHeightKey(consumerChainID string) []byte {
return append([]byte{EquivocationEvidenceMinHeightBytePrefix}, []byte(consumerChainID)...)
}

// NOTE: DO NOT ADD FULLY DEFINED KEY FUNCTIONS WITHOUT ADDING THEM TO getAllFullyDefinedKeys() IN keys_test.go

//
Expand Down
2 changes: 2 additions & 0 deletions x/ccv/provider/types/keys_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ func getAllKeyPrefixes() []byte {
providertypes.ConsumerAddrsToPruneBytePrefix,
providertypes.SlashLogBytePrefix,
providertypes.VSCMaturedHandledThisBlockBytePrefix,
providertypes.EquivocationEvidenceMinHeightBytePrefix,
}
}

Expand Down Expand Up @@ -96,6 +97,7 @@ func getAllFullyDefinedKeys() [][]byte {
providertypes.ConsumerAddrsToPruneKey("chainID", 88),
providertypes.SlashLogKey(providertypes.NewProviderConsAddress([]byte{0x05})),
providertypes.VSCMaturedHandledThisBlockKey(),
providertypes.EquivocationEvidenceMinHeightKey("chainID"),
}
}

Expand Down

0 comments on commit 1466116

Please sign in to comment.