package keeper import ( "fmt" "github.com/gogo/protobuf/proto" tmbytes "github.com/tendermint/tendermint/libs/bytes" "github.com/tendermint/tendermint/libs/log" "github.com/cosmos/cosmos-sdk/codec" codectypes "github.com/cosmos/cosmos-sdk/codec/types" "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/x/evidence/exported" "github.com/cosmos/cosmos-sdk/x/evidence/types" ) // Keeper defines the evidence module's keeper. The keeper is responsible for // managing persistence, state transitions and query handling for the evidence // module. type Keeper struct { cdc codec.Marshaler storeKey sdk.StoreKey router types.Router stakingKeeper types.StakingKeeper slashingKeeper types.SlashingKeeper } func NewKeeper( cdc codec.Marshaler, storeKey sdk.StoreKey, stakingKeeper types.StakingKeeper, slashingKeeper types.SlashingKeeper, ) *Keeper { return &Keeper{ cdc: cdc, storeKey: storeKey, stakingKeeper: stakingKeeper, slashingKeeper: slashingKeeper, } } // Logger returns a module-specific logger. func (k Keeper) Logger(ctx sdk.Context) log.Logger { return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName)) } // SetRouter sets the Evidence Handler router for the x/evidence module. Note, // we allow the ability to set the router after the Keeper is constructed as a // given Handler may need access the Keeper before being constructed. The router // may only be set once and will be sealed if it's not already sealed. func (k *Keeper) SetRouter(rtr types.Router) { // It is vital to seal the Evidence Handler router as to not allow further // handlers to be registered after the keeper is created since this // could create invalid or non-deterministic behavior. if !rtr.Sealed() { rtr.Seal() } if k.router != nil { panic(fmt.Sprintf("attempting to reset router on x/%s", types.ModuleName)) } k.router = rtr } // GetEvidenceHandler returns a registered Handler for a given Evidence type. If // no handler exists, an error is returned. func (k Keeper) GetEvidenceHandler(evidenceRoute string) (types.Handler, error) { if !k.router.HasRoute(evidenceRoute) { return nil, sdkerrors.Wrap(types.ErrNoEvidenceHandlerExists, evidenceRoute) } return k.router.GetRoute(evidenceRoute), nil } // SubmitEvidence attempts to match evidence against the keepers router and execute // the corresponding registered Evidence Handler. An error is returned if no // registered Handler exists or if the Handler fails. Otherwise, the evidence is // persisted. func (k Keeper) SubmitEvidence(ctx sdk.Context, evidence exported.Evidence) error { if _, ok := k.GetEvidence(ctx, evidence.Hash()); ok { return sdkerrors.Wrap(types.ErrEvidenceExists, evidence.Hash().String()) } if !k.router.HasRoute(evidence.Route()) { return sdkerrors.Wrap(types.ErrNoEvidenceHandlerExists, evidence.Route()) } handler := k.router.GetRoute(evidence.Route()) if err := handler(ctx, evidence); err != nil { return sdkerrors.Wrap(types.ErrInvalidEvidence, err.Error()) } ctx.EventManager().EmitEvent( sdk.NewEvent( types.EventTypeSubmitEvidence, sdk.NewAttribute(types.AttributeKeyEvidenceHash, evidence.Hash().String()), ), ) k.SetEvidence(ctx, evidence) return nil } // SetEvidence sets Evidence by hash in the module's KVStore. func (k Keeper) SetEvidence(ctx sdk.Context, evidence exported.Evidence) { store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixEvidence) store.Set(evidence.Hash(), k.MustMarshalEvidence(evidence)) } // GetEvidence retrieves Evidence by hash if it exists. If no Evidence exists for // the given hash, (nil, false) is returned. func (k Keeper) GetEvidence(ctx sdk.Context, hash tmbytes.HexBytes) (exported.Evidence, bool) { store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixEvidence) bz := store.Get(hash) if len(bz) == 0 { return nil, false } return k.MustUnmarshalEvidence(bz), true } // IterateEvidence provides an interator over all stored Evidence objects. For // each Evidence object, cb will be called. If the cb returns true, the iterator // will close and stop. func (k Keeper) IterateEvidence(ctx sdk.Context, cb func(exported.Evidence) bool) { store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixEvidence) iterator := sdk.KVStorePrefixIterator(store, nil) defer iterator.Close() for ; iterator.Valid(); iterator.Next() { evidence := k.MustUnmarshalEvidence(iterator.Value()) if cb(evidence) { break } } } // GetAllEvidence returns all stored Evidence objects. func (k Keeper) GetAllEvidence(ctx sdk.Context) (evidence []exported.Evidence) { k.IterateEvidence(ctx, func(e exported.Evidence) bool { evidence = append(evidence, e) return false }) return evidence } // MustUnmarshalEvidence attempts to decode and return an Evidence object from // raw encoded bytes. It panics on error. func (k Keeper) MustUnmarshalEvidence(bz []byte) exported.Evidence { evidence, err := k.UnmarshalEvidence(bz) if err != nil { panic(fmt.Errorf("failed to decode evidence: %w", err)) } return evidence } // MustMarshalEvidence attempts to encode an Evidence object and returns the // raw encoded bytes. It panics on error. func (k Keeper) MustMarshalEvidence(evidence exported.Evidence) []byte { bz, err := k.MarshalEvidence(evidence) if err != nil { panic(fmt.Errorf("failed to encode evidence: %w", err)) } return bz } // MarshalEvidence marshals an Evidence interface. If the given type implements // the Marshaler interface, it is treated as a Proto-defined message and // serialized that way. Otherwise, it falls back on the internal Amino codec. func (k Keeper) MarshalEvidence(evidenceI exported.Evidence) ([]byte, error) { return codec.MarshalAny(k.cdc, evidenceI) } // UnmarshalEvidence returns an Evidence interface from raw encoded evidence // bytes of a Proto-based Evidence type. An error is returned upon decoding // failure. func (k Keeper) UnmarshalEvidence(bz []byte) (exported.Evidence, error) { var evi exported.Evidence if err := codec.UnmarshalAny(k.cdc, &evi, bz); err != nil { return nil, err } return evi, nil } // MarshalEvidenceJSON JSON encodes an evidence object implementing the Evidence // interface. func (k Keeper) MarshalEvidenceJSON(evidence exported.Evidence) ([]byte, error) { msg, ok := evidence.(proto.Message) if !ok { return nil, fmt.Errorf("cannot proto marshal %T", evidence) } any, err := codectypes.NewAnyWithValue(msg) if err != nil { return nil, err } return k.cdc.MarshalJSON(any) } // UnmarshalEvidenceJSON returns an Evidence from JSON encoded bytes func (k Keeper) UnmarshalEvidenceJSON(bz []byte) (exported.Evidence, error) { var any codectypes.Any if err := k.cdc.UnmarshalJSON(bz, &any); err != nil { return nil, err } var evi exported.Evidence if err := k.cdc.UnpackAny(&any, &evi); err != nil { return nil, err } return evi, nil }