|
4 | 4 | "bytes"
|
5 | 5 | "encoding/binary"
|
6 | 6 | "fmt"
|
| 7 | + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" |
7 | 8 |
|
8 | 9 | ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types"
|
9 | 10 | ibctmtypes "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint"
|
@@ -35,6 +36,27 @@ func (k Keeper) HandleConsumerDoubleVoting(
|
35 | 36 | chainID string,
|
36 | 37 | pubkey cryptotypes.PubKey,
|
37 | 38 | ) 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 | + |
38 | 60 | // verifies the double voting evidence using the consumer chain public key
|
39 | 61 | if err := k.VerifyDoubleVotingEvidence(*evidence, chainID, pubkey); err != nil {
|
40 | 62 | return err
|
@@ -269,34 +291,51 @@ func headerToLightBlock(h ibctmtypes.Header) (*tmtypes.LightBlock, error) {
|
269 | 291 | }
|
270 | 292 |
|
271 | 293 | // 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 |
273 | 295 | func (k Keeper) CheckMisbehaviour(ctx sdk.Context, misbehaviour ibctmtypes.Misbehaviour) error {
|
| 296 | + consumerChainID := misbehaviour.Header1.Header.ChainID |
| 297 | + |
274 | 298 | // 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) |
276 | 300 | 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) |
278 | 302 | } else if misbehaviour.ClientId != clientId {
|
279 | 303 | return fmt.Errorf("incorrect misbehaviour: expected client ID for consumer chain %s is %s got %s",
|
280 |
| - misbehaviour.Header1.Header.ChainID, |
| 304 | + consumerChainID, |
281 | 305 | clientId,
|
282 | 306 | misbehaviour.ClientId,
|
283 | 307 | )
|
284 | 308 | }
|
285 | 309 |
|
| 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 | + |
286 | 332 | clientState, found := k.clientKeeper.GetClientState(ctx, clientId)
|
287 | 333 | if !found {
|
288 | 334 | return errorsmod.Wrapf(ibcclienttypes.ErrClientNotFound, "cannot find client state for client with ID %s", clientId)
|
289 | 335 | }
|
290 | 336 |
|
291 | 337 | clientStore := k.clientKeeper.ClientStore(ctx, clientId)
|
292 | 338 |
|
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 |
| - |
300 | 339 | // CheckForMisbehaviour verifies that the headers have different blockID hashes
|
301 | 340 | ok := clientState.CheckForMisbehaviour(ctx, k.cdc, clientStore, &misbehaviour)
|
302 | 341 | if !ok {
|
|
0 commit comments