From 2d0b5fdafbfb6d643be8876c4f4d5159716de980 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Thu, 24 Oct 2019 13:46:32 -0400 Subject: [PATCH 01/69] add stub files for evidence module --- x/evidence/abci.go | 1 + x/evidence/alias.go | 1 + x/evidence/genesis.go | 1 + x/evidence/module.go | 1 + 4 files changed, 4 insertions(+) create mode 100644 x/evidence/abci.go create mode 100644 x/evidence/alias.go create mode 100644 x/evidence/genesis.go create mode 100644 x/evidence/module.go diff --git a/x/evidence/abci.go b/x/evidence/abci.go new file mode 100644 index 000000000000..fc422ea0ab90 --- /dev/null +++ b/x/evidence/abci.go @@ -0,0 +1 @@ +package evidence diff --git a/x/evidence/alias.go b/x/evidence/alias.go new file mode 100644 index 000000000000..fc422ea0ab90 --- /dev/null +++ b/x/evidence/alias.go @@ -0,0 +1 @@ +package evidence diff --git a/x/evidence/genesis.go b/x/evidence/genesis.go new file mode 100644 index 000000000000..fc422ea0ab90 --- /dev/null +++ b/x/evidence/genesis.go @@ -0,0 +1 @@ +package evidence diff --git a/x/evidence/module.go b/x/evidence/module.go new file mode 100644 index 000000000000..fc422ea0ab90 --- /dev/null +++ b/x/evidence/module.go @@ -0,0 +1 @@ +package evidence From 9e1a72624026a0dfe26456d1b86d6d72666017c0 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Thu, 24 Oct 2019 13:53:43 -0400 Subject: [PATCH 02/69] adr: update status and log --- docs/architecture/adr-009-evidence-module.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/architecture/adr-009-evidence-module.md b/docs/architecture/adr-009-evidence-module.md index ac73d60a3df1..a696ed758e2a 100644 --- a/docs/architecture/adr-009-evidence-module.md +++ b/docs/architecture/adr-009-evidence-module.md @@ -3,10 +3,11 @@ ## Changelog - 2019 July 31: Initial draft +- 2019 October 24: Initial implementation ## Status -Proposed +Accepted ## Context From fff7429630284f6b112373088d2459b7e5547c7b Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Thu, 24 Oct 2019 15:24:24 -0400 Subject: [PATCH 03/69] adr: update router definition --- docs/architecture/adr-009-evidence-module.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/architecture/adr-009-evidence-module.md b/docs/architecture/adr-009-evidence-module.md index a696ed758e2a..73d02f1f30bb 100644 --- a/docs/architecture/adr-009-evidence-module.md +++ b/docs/architecture/adr-009-evidence-module.md @@ -79,7 +79,7 @@ the `x/evidence` module. It accomplishes this through the `Router` implementatio ```go type Router interface { - AddRoute(r string, h Handler) + AddRoute(r string, h Handler) Router HasRoute(r string) bool GetRoute(path string) Handler Seal() From 9e92430d933a23fd3a0a7d1a961b5a61fc8cd4da Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Thu, 24 Oct 2019 15:24:52 -0400 Subject: [PATCH 04/69] move IsAlphaNumeric to types/ and update gov usage --- types/router.go | 6 ++++++ x/gov/types/router.go | 4 +++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/types/router.go b/types/router.go index 7b45918c3cc6..c14255d4ec76 100644 --- a/types/router.go +++ b/types/router.go @@ -1,5 +1,11 @@ package types +import "regexp" + +// IsAlphaNumeric defines a regular expression for matching against alpha-numeric +// values. +var IsAlphaNumeric = regexp.MustCompile(`^[a-zA-Z0-9]+$`).MatchString + // Router provides handlers for each transaction type. type Router interface { AddRoute(r string, h Handler) Router diff --git a/x/gov/types/router.go b/x/gov/types/router.go index 3901d8869e9a..651d20dc9dfa 100644 --- a/x/gov/types/router.go +++ b/x/gov/types/router.go @@ -3,6 +3,8 @@ package types import ( "fmt" "regexp" + + sdk "github.com/cosmos/cosmos-sdk/types" ) var ( @@ -49,7 +51,7 @@ func (rtr *router) AddRoute(path string, h Handler) Router { panic("router sealed; cannot add route handler") } - if !isAlphaNumeric(path) { + if !sdk.IsAlphaNumeric(path) { panic("route expressions can only contain alphanumeric characters") } if rtr.HasRoute(path) { From b3cd14e7b5a9c10bee2bd7a298c9191695d9f6d6 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Thu, 24 Oct 2019 15:25:14 -0400 Subject: [PATCH 05/69] types: add evidence type and router/handler --- x/evidence/internal/types/evidence.go | 27 ++++++++++ x/evidence/internal/types/router.go | 75 +++++++++++++++++++++++++++ 2 files changed, 102 insertions(+) create mode 100644 x/evidence/internal/types/evidence.go create mode 100644 x/evidence/internal/types/router.go diff --git a/x/evidence/internal/types/evidence.go b/x/evidence/internal/types/evidence.go new file mode 100644 index 000000000000..213fc0bbba71 --- /dev/null +++ b/x/evidence/internal/types/evidence.go @@ -0,0 +1,27 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +// Evidence defines the contract which concrete evidence types of misbehavior +// must implement. +type Evidence interface { + Route() string + Type() string + String() string + ValidateBasic() sdkerrors.Error + + // The consensus address of the malicious validator at time of infraction + GetConsensusAddress() sdk.ConsAddress + + // Height at which the infraction occurred + GetHeight() int64 + + // The total power of the malicious validator at time of infraction + GetValidatorPower() int64 + + // The total validator set power at time of infraction + GetTotalPower() int64 +} diff --git a/x/evidence/internal/types/router.go b/x/evidence/internal/types/router.go new file mode 100644 index 000000000000..ad851ad852c7 --- /dev/null +++ b/x/evidence/internal/types/router.go @@ -0,0 +1,75 @@ +package types + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +type ( + // Handler defines an agnostic Evidence handler. The handler is responsible + // for executing all corresponding business logic necessary for verifying the + // evidence as valid. In addition, the Handler may execute any necessary + // slashing and potential jailing. + Handler func(sdk.Context, Evidence) sdkerrors.Error + + // Router defines a contract for which any Evidence handling module must + // implement in order to route Evidence to registered Handlers. + Router interface { + AddRoute(r string, h Handler) Router + HasRoute(r string) bool + GetRoute(path string) Handler + Seal() + } + + router struct { + routes map[string]Handler + sealed bool + } +) + +func NewRouter() Router { + return &router{ + routes: make(map[string]Handler), + } +} + +// Seal prevents the router from any subsequent route handlers to be registered. +// Seal will panic if called more than once. +func (rtr *router) Seal() { + if rtr.sealed { + panic("router already sealed") + } + rtr.sealed = true +} + +// AddRoute adds a governance handler for a given path. It returns the Router +// so AddRoute calls can be linked. It will panic if the router is sealed. +func (rtr *router) AddRoute(path string, h Handler) Router { + if rtr.sealed { + panic(fmt.Sprintf("router sealed; cannot register %s route handler", path)) + } + if !sdk.IsAlphaNumeric(path) { + panic("route expressions can only contain alphanumeric characters") + } + if rtr.HasRoute(path) { + panic(fmt.Sprintf("route %s has already been registered", path)) + } + + rtr.routes[path] = h + return rtr +} + +// HasRoute returns true if the router has a path registered or false otherwise. +func (rtr *router) HasRoute(path string) bool { + return rtr.routes[path] != nil +} + +// GetRoute returns a Handler for a given path. +func (rtr *router) GetRoute(path string) Handler { + if !rtr.HasRoute(path) { + panic(fmt.Sprintf("route does not exist for path %s", path)) + } + return rtr.routes[path] +} From 2d5b6a7ab5199a2f27fba2dbdce20b81904d3687 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Thu, 24 Oct 2019 15:33:29 -0400 Subject: [PATCH 06/69] implement EvidenceHandler and related types --- x/evidence/client/proposal_handler.go | 31 +++++++++++++++++++++++++++ x/evidence/client/rest/rest.go | 12 +++++++++++ 2 files changed, 43 insertions(+) create mode 100644 x/evidence/client/proposal_handler.go create mode 100644 x/evidence/client/rest/rest.go diff --git a/x/evidence/client/proposal_handler.go b/x/evidence/client/proposal_handler.go new file mode 100644 index 000000000000..0b56b4c4cbf1 --- /dev/null +++ b/x/evidence/client/proposal_handler.go @@ -0,0 +1,31 @@ +package client + +import ( + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/x/evidence/client/rest" +) + +type ( + // RESTHandlerFn defines a REST service handler for evidence submission + RESTHandlerFn func(context.CLIContext) rest.EvidenceRESTHandler + + // CLIHandlerFn defines a CLI command handler for evidence submission + CLIHandlerFn func(*codec.Codec) *cobra.Command + + // EvidenceHandler defines a type that exposes REST and CLI client handlers for + // evidence submission. + EvidenceHandler struct { + CLIHandler CLIHandlerFn + RESTHandler RESTHandlerFn + } +) + +func NewEvidenceHandler(cliHandler CLIHandlerFn, restHandler RESTHandlerFn) EvidenceHandler { + return EvidenceHandler{ + CLIHandler: cliHandler, + RESTHandler: restHandler, + } +} diff --git a/x/evidence/client/rest/rest.go b/x/evidence/client/rest/rest.go new file mode 100644 index 000000000000..5a8873ca3773 --- /dev/null +++ b/x/evidence/client/rest/rest.go @@ -0,0 +1,12 @@ +package rest + +import ( + "net/http" +) + +// EvidenceRESTHandler defines a REST service evidence handler implemented in +// another module. The sub-route is mounted on the evidence REST handler. +type EvidenceRESTHandler struct { + SubRoute string + Handler func(http.ResponseWriter, *http.Request) +} From 30af5af2405dd250fc352e5204bb65886f20f464 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Thu, 24 Oct 2019 15:35:14 -0400 Subject: [PATCH 07/69] rename file --- x/evidence/client/{proposal_handler.go => evidence_handler.go} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename x/evidence/client/{proposal_handler.go => evidence_handler.go} (100%) diff --git a/x/evidence/client/proposal_handler.go b/x/evidence/client/evidence_handler.go similarity index 100% rename from x/evidence/client/proposal_handler.go rename to x/evidence/client/evidence_handler.go From 45db95ef67bcd4878f1b071e571d33ab440a0d8e Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Thu, 24 Oct 2019 15:40:32 -0400 Subject: [PATCH 08/69] add module constants --- x/evidence/internal/types/keys.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 x/evidence/internal/types/keys.go diff --git a/x/evidence/internal/types/keys.go b/x/evidence/internal/types/keys.go new file mode 100644 index 000000000000..c8a081a8c4c0 --- /dev/null +++ b/x/evidence/internal/types/keys.go @@ -0,0 +1,18 @@ +package types + +const ( + // ModuleName defines the module name + ModuleName = "evidence" + + // StoreKey defines the primary module store key + StoreKey = ModuleName + + // RouterKey defines the module's message routing key + RouterKey = ModuleName + + // QuerierRoute defines the module's query routing key + QuerierRoute = ModuleName + + // DefaultParamspace defines the module's default paramspace name + DefaultParamspace = ModuleName +) From eee71d2211eb2f300f9ab02660dd145507736929 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Thu, 24 Oct 2019 15:45:28 -0400 Subject: [PATCH 09/69] add initial codec --- x/evidence/internal/types/codec.go | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 x/evidence/internal/types/codec.go diff --git a/x/evidence/internal/types/codec.go b/x/evidence/internal/types/codec.go new file mode 100644 index 000000000000..89a536ed6657 --- /dev/null +++ b/x/evidence/internal/types/codec.go @@ -0,0 +1,28 @@ +package types + +import ( + "github.com/cosmos/cosmos-sdk/codec" +) + +// ModuleCdc defines the evidence module's codec. The codec is not sealed as to +// allow other modules to register their concrete Evidence types. +var ModuleCdc = codec.New() + +// RegisterCodec registers all the necessary types and interfaces for the +// evidence module. +func RegisterCodec(cdc *codec.Codec) { + cdc.RegisterInterface((*Evidence)(nil), nil) + + // TODO: Register concrete evidence types. +} + +// RegisterEvidenceTypeCodec registers an external concrete Evidence type defined +// in another module for the internal ModuleCdc. This allows the MsgSubmitEvidence +// to be correctly Amino encoded and decoded. +func RegisterEvidenceTypeCodec(o interface{}, name string) { + ModuleCdc.RegisterConcrete(o, name, nil) +} + +func init() { + RegisterCodec(ModuleCdc) +} From 6c102f41483db14e5f61ffd7e5dac5cd6d000e05 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Thu, 24 Oct 2019 16:28:11 -0400 Subject: [PATCH 10/69] update client package to be used by AppModuleBasic --- x/evidence/client/cli/query.go | 24 +++++++++++++++++++++ x/evidence/client/cli/tx.go | 38 +++++++++++++++++++++++++++++++++ x/evidence/client/rest/query.go | 11 ++++++++++ x/evidence/client/rest/rest.go | 11 ++++++++++ x/evidence/client/rest/tx.go | 11 ++++++++++ 5 files changed, 95 insertions(+) create mode 100644 x/evidence/client/cli/query.go create mode 100644 x/evidence/client/cli/tx.go create mode 100644 x/evidence/client/rest/query.go create mode 100644 x/evidence/client/rest/tx.go diff --git a/x/evidence/client/cli/query.go b/x/evidence/client/cli/query.go new file mode 100644 index 000000000000..301ab41be844 --- /dev/null +++ b/x/evidence/client/cli/query.go @@ -0,0 +1,24 @@ +package cli + +import ( + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/x/evidence/internal/types" + "github.com/spf13/cobra" +) + +// GetQueryCmd returns the CLI command with all evidence module query commands +// mounted. +func GetQueryCmd(queryRoute string, cdc *codec.Codec) *cobra.Command { + cmd := &cobra.Command{ + Use: types.ModuleName, + Short: "Evidence query subcommands", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + // TODO: Add query commands. + + return cmd +} diff --git a/x/evidence/client/cli/tx.go b/x/evidence/client/cli/tx.go new file mode 100644 index 000000000000..da03bad8d7e0 --- /dev/null +++ b/x/evidence/client/cli/tx.go @@ -0,0 +1,38 @@ +package cli + +import ( + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/x/evidence/internal/types" + + "github.com/spf13/cobra" +) + +// GetTxCmd returns a CLI command that has all the native evidence module tx +// commands mounted. In addition, it mounts all childCmds, implemented by outside +// modules, under a sub-command. This allows external modules to implement custom +// Evidence types and Handlers while having the ability to create and sign txs +// containing them all from a single root command. +func GetTxCmd(storeKey string, cdc *codec.Codec, childCmds []*cobra.Command) *cobra.Command { + cmd := &cobra.Command{ + Use: types.ModuleName, + Short: "Evidence transaction subcommands", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + submitEvidenceCmd := getSubmitEvidenceCmd(cdc) + for _, childCmd := range childCmds { + submitEvidenceCmd.AddCommand(client.PostCommands(childCmd)[0]) + } + + // TODO: Add tx commands. + + return cmd +} + +func getSubmitEvidenceCmd(cdc *codec.Codec) *cobra.Command { + // TODO: Implement and return 'submit-evidence sub-command' + return nil +} diff --git a/x/evidence/client/rest/query.go b/x/evidence/client/rest/query.go new file mode 100644 index 000000000000..73b5d4a42282 --- /dev/null +++ b/x/evidence/client/rest/query.go @@ -0,0 +1,11 @@ +package rest + +import ( + "github.com/cosmos/cosmos-sdk/client/context" + + "github.com/gorilla/mux" +) + +func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router) { + // TODO: Register query handlers. +} diff --git a/x/evidence/client/rest/rest.go b/x/evidence/client/rest/rest.go index 5a8873ca3773..737f0a3ddc35 100644 --- a/x/evidence/client/rest/rest.go +++ b/x/evidence/client/rest/rest.go @@ -2,6 +2,10 @@ package rest import ( "net/http" + + "github.com/cosmos/cosmos-sdk/client/context" + + "github.com/gorilla/mux" ) // EvidenceRESTHandler defines a REST service evidence handler implemented in @@ -10,3 +14,10 @@ type EvidenceRESTHandler struct { SubRoute string Handler func(http.ResponseWriter, *http.Request) } + +// RegisterRoutes registers all Evidence submission handlers for the evidence module's +// REST service handler. +func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, handlers []EvidenceRESTHandler) { + registerQueryRoutes(cliCtx, r) + registerTxRoutes(cliCtx, r, handlers) +} diff --git a/x/evidence/client/rest/tx.go b/x/evidence/client/rest/tx.go new file mode 100644 index 000000000000..b053e4fca6b8 --- /dev/null +++ b/x/evidence/client/rest/tx.go @@ -0,0 +1,11 @@ +package rest + +import ( + "github.com/cosmos/cosmos-sdk/client/context" + + "github.com/gorilla/mux" +) + +func registerTxRoutes(cliCtx context.CLIContext, r *mux.Router, handlers []EvidenceRESTHandler) { + // TODO: Register tx handlers. +} From e8851a63c4805b1e86e4eb182178042c574848ac Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Thu, 24 Oct 2019 16:30:23 -0400 Subject: [PATCH 11/69] update alias' --- x/evidence/alias.go | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/x/evidence/alias.go b/x/evidence/alias.go index fc422ea0ab90..4eaa35d8517a 100644 --- a/x/evidence/alias.go +++ b/x/evidence/alias.go @@ -1 +1,25 @@ package evidence + +import ( + "github.com/cosmos/cosmos-sdk/x/evidence/internal/types" +) + +const ( + ModuleName = types.ModuleName + StoreKey = types.StoreKey + RouterKey = types.RouterKey + QuerierRoute = types.QuerierRoute + DefaultParamspace = types.DefaultParamspace +) + +var ( + RegisterCodec = types.RegisterCodec + RegisterEvidenceTypeCodec = types.RegisterEvidenceTypeCodec + ModuleCdc = types.ModuleCdc +) + +type ( + Evidence = types.Evidence + Handler = types.Handler + Router = types.Router +) From 1f33d38033f2dc23b13c3fc2244bb7ca53dd526d Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Thu, 24 Oct 2019 16:31:14 -0400 Subject: [PATCH 12/69] nolint alias file --- x/evidence/alias.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/x/evidence/alias.go b/x/evidence/alias.go index 4eaa35d8517a..42fa657c962f 100644 --- a/x/evidence/alias.go +++ b/x/evidence/alias.go @@ -4,6 +4,8 @@ import ( "github.com/cosmos/cosmos-sdk/x/evidence/internal/types" ) +// nolint + const ( ModuleName = types.ModuleName StoreKey = types.StoreKey From 5a27e5b3738f75cadc52a7d47506c3ad2dfb8c6f Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Thu, 24 Oct 2019 16:31:54 -0400 Subject: [PATCH 13/69] implement AppModuleBasic --- x/evidence/client/cli/query.go | 3 +- x/evidence/module.go | 88 ++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 1 deletion(-) diff --git a/x/evidence/client/cli/query.go b/x/evidence/client/cli/query.go index 301ab41be844..6949f5a329ef 100644 --- a/x/evidence/client/cli/query.go +++ b/x/evidence/client/cli/query.go @@ -1,10 +1,11 @@ package cli import ( + "github.com/spf13/cobra" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/x/evidence/internal/types" - "github.com/spf13/cobra" ) // GetQueryCmd returns the CLI command with all evidence module query commands diff --git a/x/evidence/module.go b/x/evidence/module.go index fc422ea0ab90..43c76652b38a 100644 --- a/x/evidence/module.go +++ b/x/evidence/module.go @@ -1 +1,89 @@ package evidence + +import ( + "encoding/json" + + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/types/module" + "github.com/cosmos/cosmos-sdk/x/evidence/client" + "github.com/cosmos/cosmos-sdk/x/evidence/client/cli" + "github.com/cosmos/cosmos-sdk/x/evidence/client/rest" + + "github.com/gorilla/mux" + "github.com/spf13/cobra" +) + +var ( + // _ module.AppModule = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} + // _ module.AppModuleSimulation = AppModuleSimulation{} +) + +// ---------------------------------------------------------------------------- +// AppModuleBasic +// ---------------------------------------------------------------------------- + +// AppModuleBasic implements the AppModuleBasic interface for the evidence module. +type AppModuleBasic struct { + evidenceHandlers []client.EvidenceHandler // client evidence submission handlers +} + +func NewAppModuleBasic(evidenceHandlers ...client.EvidenceHandler) AppModuleBasic { + return AppModuleBasic{ + evidenceHandlers: evidenceHandlers, + } +} + +// Name returns the evidence module's name. +func (AppModuleBasic) Name() string { + return ModuleName +} + +// RegisterCodec registers the evidence module's types to the provided codec. +func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) { + RegisterCodec(cdc) +} + +// DefaultGenesis returns the evidence module's default genesis state. +func (AppModuleBasic) DefaultGenesis() json.RawMessage { + // TODO: Return proper default genesis state. + return []byte("[]") +} + +// ValidateGenesis performs genesis state validation for the evidence module. +func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { + // TODO: Validate genesis state. + return nil +} + +// RegisterRESTRoutes registers the evidence module's REST service handlers. +func (a AppModuleBasic) RegisterRESTRoutes(ctx context.CLIContext, rtr *mux.Router) { + evidenceRESTHandlers := make([]rest.EvidenceRESTHandler, len(a.evidenceHandlers)) + + for i, evidenceHandler := range a.evidenceHandlers { + evidenceRESTHandlers[i] = evidenceHandler.RESTHandler(ctx) + } + + rest.RegisterRoutes(ctx, rtr, evidenceRESTHandlers) +} + +// GetTxCmd returns the evidence module's root tx command. +func (a AppModuleBasic) GetTxCmd(cdc *codec.Codec) *cobra.Command { + evidenceCLIHandlers := make([]*cobra.Command, len(a.evidenceHandlers)) + + for i, evidenceHandler := range a.evidenceHandlers { + evidenceCLIHandlers[i] = evidenceHandler.CLIHandler(cdc) + } + + return cli.GetTxCmd(StoreKey, cdc, evidenceCLIHandlers) +} + +// GetTxCmd returns the evidence module's root query command. +func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command { + return cli.GetQueryCmd(StoreKey, cdc) +} + +// ---------------------------------------------------------------------------- +// AppModule +// ---------------------------------------------------------------------------- From 46a1fb81ceac218ff065d61a8aa7144c53736dd4 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Fri, 25 Oct 2019 08:36:14 -0400 Subject: [PATCH 14/69] update AppModule --- x/evidence/module.go | 73 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 72 insertions(+), 1 deletion(-) diff --git a/x/evidence/module.go b/x/evidence/module.go index 43c76652b38a..2202cf65429f 100644 --- a/x/evidence/module.go +++ b/x/evidence/module.go @@ -5,17 +5,20 @@ import ( "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" "github.com/cosmos/cosmos-sdk/x/evidence/client" "github.com/cosmos/cosmos-sdk/x/evidence/client/cli" "github.com/cosmos/cosmos-sdk/x/evidence/client/rest" + abci "github.com/tendermint/tendermint/abci/types" + "github.com/gorilla/mux" "github.com/spf13/cobra" ) var ( - // _ module.AppModule = AppModule{} + _ module.AppModule = AppModule{} _ module.AppModuleBasic = AppModuleBasic{} // _ module.AppModuleSimulation = AppModuleSimulation{} ) @@ -87,3 +90,71 @@ func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command { // ---------------------------------------------------------------------------- // AppModule // ---------------------------------------------------------------------------- + +// AppModule implements the AppModule interface for the evidence module. +type AppModule struct { + AppModuleBasic +} + +func NewAppModule() AppModule { + return AppModule{ + AppModuleBasic: NewAppModuleBasic(), + } +} + +// Name returns the evidence module's name. +func (am AppModule) Name() string { + return am.AppModuleBasic.Name() +} + +// Route returns the evidence module's message routing key. +func (AppModule) Route() string { + return RouterKey +} + +// QuerierRoute returns the evidence module's query routing key. +func (AppModule) QuerierRoute() string { + return QuerierRoute +} + +// RegisterInvariants registers the evidence module's invariants. +func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) { + // TODO: Register any necessary invariants (if any). +} + +// NewHandler returns the evidence module's message Handler. +func (am AppModule) NewHandler() sdk.Handler { + // TODO: Construct and return message Handler. + return nil +} + +// NewQuerierHandler returns the evidence module's Querier. +func (am AppModule) NewQuerierHandler() sdk.Querier { + // TODO: Construct and return Querier. + return nil +} + +// InitGenesis performs the evidence module's genesis initialization It returns +// no validator updates. +func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.ValidatorUpdate { + // TODO: Construct genesis state object and deserialize. + return []abci.ValidatorUpdate{} +} + +// ExportGenesis returns the evidence module's exported genesis state as raw JSON bytes. +func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage { + // TODO: Export genesis state and serialize. + return []byte("[]") +} + +// BeginBlock executes all ABCI BeginBlock logic respective to the evidence module. +func (AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) { + // TODO: Execute BeginBlocker (if applicable). +} + +// EndBlock executes all ABCI EndBlock logic respective to the evidence module. It +// returns no validator updates. +func (am AppModule) EndBlock(ctx sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { + // TODO: Execute EndBlocker (if applicable). + return []abci.ValidatorUpdate{} +} From febbbf5b0560e122f4b7d62ca832dd547d2e34a9 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Fri, 25 Oct 2019 09:46:41 -0400 Subject: [PATCH 15/69] add unit tests for types --- x/evidence/internal/types/codec_test.go | 38 ++++++++++++++++++++++++ x/evidence/internal/types/evidence.go | 3 +- x/evidence/internal/types/router.go | 3 +- x/evidence/internal/types/router_test.go | 26 ++++++++++++++++ 4 files changed, 66 insertions(+), 4 deletions(-) create mode 100644 x/evidence/internal/types/codec_test.go create mode 100644 x/evidence/internal/types/router_test.go diff --git a/x/evidence/internal/types/codec_test.go b/x/evidence/internal/types/codec_test.go new file mode 100644 index 000000000000..fb4f3e9f1dae --- /dev/null +++ b/x/evidence/internal/types/codec_test.go @@ -0,0 +1,38 @@ +package types_test + +import ( + "testing" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/evidence/internal/types" + "github.com/stretchr/testify/require" +) + +var _ types.Evidence = (*testEvidence)(nil) + +type testEvidence struct{} + +func (te testEvidence) Route() string { return "" } +func (te testEvidence) Type() string { return "" } +func (te testEvidence) String() string { return "" } +func (te testEvidence) ValidateBasic() error { return nil } +func (te testEvidence) GetConsensusAddress() sdk.ConsAddress { return nil } +func (te testEvidence) GetHeight() int64 { return 0 } +func (te testEvidence) GetValidatorPower() int64 { return 0 } +func (te testEvidence) GetTotalPower() int64 { return 0 } + +func TestCodec(t *testing.T) { + cdc := codec.New() + types.RegisterCodec(cdc) + types.RegisterEvidenceTypeCodec(testEvidence{}, "cosmos-sdk/testEvidence") + + var e types.Evidence = testEvidence{} + bz, err := cdc.MarshalBinaryBare(e) + require.NoError(t, err) + + var te testEvidence + require.NoError(t, cdc.UnmarshalBinaryBare(bz, &te)) + + require.Panics(t, func() { types.RegisterEvidenceTypeCodec(testEvidence{}, "cosmos-sdk/testEvidence") }) +} diff --git a/x/evidence/internal/types/evidence.go b/x/evidence/internal/types/evidence.go index 213fc0bbba71..dcafae8d8329 100644 --- a/x/evidence/internal/types/evidence.go +++ b/x/evidence/internal/types/evidence.go @@ -2,7 +2,6 @@ package types import ( sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) // Evidence defines the contract which concrete evidence types of misbehavior @@ -11,7 +10,7 @@ type Evidence interface { Route() string Type() string String() string - ValidateBasic() sdkerrors.Error + ValidateBasic() error // The consensus address of the malicious validator at time of infraction GetConsensusAddress() sdk.ConsAddress diff --git a/x/evidence/internal/types/router.go b/x/evidence/internal/types/router.go index ad851ad852c7..5389353e36cc 100644 --- a/x/evidence/internal/types/router.go +++ b/x/evidence/internal/types/router.go @@ -4,7 +4,6 @@ import ( "fmt" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) type ( @@ -12,7 +11,7 @@ type ( // for executing all corresponding business logic necessary for verifying the // evidence as valid. In addition, the Handler may execute any necessary // slashing and potential jailing. - Handler func(sdk.Context, Evidence) sdkerrors.Error + Handler func(sdk.Context, Evidence) error // Router defines a contract for which any Evidence handling module must // implement in order to route Evidence to registered Handlers. diff --git a/x/evidence/internal/types/router_test.go b/x/evidence/internal/types/router_test.go new file mode 100644 index 000000000000..fc94ad04ea9a --- /dev/null +++ b/x/evidence/internal/types/router_test.go @@ -0,0 +1,26 @@ +package types_test + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/evidence/internal/types" + "github.com/stretchr/testify/require" +) + +func testHandler(sdk.Context, types.Evidence) error { return nil } + +func TestRouterSeal(t *testing.T) { + r := types.NewRouter() + r.Seal() + require.Panics(t, func() { r.AddRoute("test", nil) }) + require.Panics(t, func() { r.Seal() }) +} + +func TestRouter(t *testing.T) { + r := types.NewRouter() + r.AddRoute("test", testHandler) + require.True(t, r.HasRoute("test")) + require.Panics(t, func() { r.AddRoute("test", testHandler) }) + require.Panics(t, func() { r.AddRoute(" ", testHandler) }) +} From e5894786a889a764dbc9369e53179c69aacad2c3 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Fri, 25 Oct 2019 09:46:54 -0400 Subject: [PATCH 16/69] adr: update error return type --- docs/architecture/adr-009-evidence-module.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/architecture/adr-009-evidence-module.md b/docs/architecture/adr-009-evidence-module.md index 73d02f1f30bb..5f58e02534f0 100644 --- a/docs/architecture/adr-009-evidence-module.md +++ b/docs/architecture/adr-009-evidence-module.md @@ -56,7 +56,7 @@ type Evidence interface { Route() string Type() string String() string - ValidateBasic() Error + ValidateBasic() error // The consensus address of the malicious validator at time of infraction GetConsensusAddress() ConsAddress @@ -98,7 +98,7 @@ necessary in order for the `Handler` to make the necessary state transitions. If no error is returned, the `Evidence` is considered valid. ```go -type Handler func(Context, Evidence) Error +type Handler func(Context, Evidence) error ``` ### Submission From e51c9d2b8481a2d5429ae88642f4f080168ecf14 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Fri, 25 Oct 2019 09:58:48 -0400 Subject: [PATCH 17/69] implement initial keeper --- x/evidence/doc.go | 1 + x/evidence/internal/keeper/keeper.go | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 x/evidence/doc.go create mode 100644 x/evidence/internal/keeper/keeper.go diff --git a/x/evidence/doc.go b/x/evidence/doc.go new file mode 100644 index 000000000000..fc422ea0ab90 --- /dev/null +++ b/x/evidence/doc.go @@ -0,0 +1 @@ +package evidence diff --git a/x/evidence/internal/keeper/keeper.go b/x/evidence/internal/keeper/keeper.go new file mode 100644 index 000000000000..29731385bae8 --- /dev/null +++ b/x/evidence/internal/keeper/keeper.go @@ -0,0 +1,24 @@ +package keeper + +import ( + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/params" +) + +// 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.Codec + storeKey sdk.StoreKey + paramSpace params.Subspace +} + +func NewKeeper(cdc *codec.Codec, storeKey sdk.StoreKey, paramSpace params.Subspace) Keeper { + return Keeper{ + cdc: cdc, + storeKey: storeKey, + paramSpace: paramSpace, + } +} From 9b764112593c3cc03f62babd2b6e9db729c335f8 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Fri, 25 Oct 2019 10:41:01 -0400 Subject: [PATCH 18/69] update adr --- docs/architecture/adr-009-evidence-module.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/architecture/adr-009-evidence-module.md b/docs/architecture/adr-009-evidence-module.md index 5f58e02534f0..1dda97ac7d5b 100644 --- a/docs/architecture/adr-009-evidence-module.md +++ b/docs/architecture/adr-009-evidence-module.md @@ -56,6 +56,7 @@ type Evidence interface { Route() string Type() string String() string + Hash() HexBytes ValidateBasic() error // The consensus address of the malicious validator at time of infraction From 37ca15fb16858df309708392803e3f66c7e9da77 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Fri, 25 Oct 2019 10:41:48 -0400 Subject: [PATCH 19/69] update evidence type to include hash --- x/evidence/internal/types/evidence.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/x/evidence/internal/types/evidence.go b/x/evidence/internal/types/evidence.go index dcafae8d8329..4e9c55d874fc 100644 --- a/x/evidence/internal/types/evidence.go +++ b/x/evidence/internal/types/evidence.go @@ -2,6 +2,8 @@ package types import ( sdk "github.com/cosmos/cosmos-sdk/types" + + cmn "github.com/tendermint/tendermint/libs/common" ) // Evidence defines the contract which concrete evidence types of misbehavior @@ -10,6 +12,7 @@ type Evidence interface { Route() string Type() string String() string + Hash() cmn.HexBytes ValidateBasic() error // The consensus address of the malicious validator at time of infraction From dbaba7ab430c14f66e4f4a3bca855fc394d6dbf9 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Fri, 25 Oct 2019 11:43:24 -0400 Subject: [PATCH 20/69] add initial error handling --- types/errors/errors.go | 6 +++++- x/evidence/alias.go | 5 +++++ x/evidence/internal/keeper/keeper.go | 28 ++++++++++++++++++++++++- x/evidence/internal/types/codec_test.go | 2 ++ 4 files changed, 39 insertions(+), 2 deletions(-) diff --git a/types/errors/errors.go b/types/errors/errors.go index 642c433d90b2..af4529f6b0f0 100644 --- a/types/errors/errors.go +++ b/types/errors/errors.go @@ -121,7 +121,7 @@ func ABCIError(codespace string, code uint32, log string) error { } // This is a unique error, will never match on .Is() // Use Wrap here to get a stack trace - return Wrap(&Error{codespace: codespace, code: code, desc: "unknown"}, log) + return Wrap(New(codespace, code, "unknown"), log) } // Error represents a root error. @@ -139,6 +139,10 @@ type Error struct { desc string } +func New(codespace string, code uint32, desc string) *Error { + return &Error{codespace: codespace, code: code, desc: desc} +} + func (e Error) Error() string { return e.desc } diff --git a/x/evidence/alias.go b/x/evidence/alias.go index 42fa657c962f..9533b2f3c206 100644 --- a/x/evidence/alias.go +++ b/x/evidence/alias.go @@ -1,6 +1,7 @@ package evidence import ( + "github.com/cosmos/cosmos-sdk/x/evidence/internal/keeper" "github.com/cosmos/cosmos-sdk/x/evidence/internal/types" ) @@ -15,12 +16,16 @@ const ( ) var ( + NewKeeper = keeper.NewKeeper + RegisterCodec = types.RegisterCodec RegisterEvidenceTypeCodec = types.RegisterEvidenceTypeCodec ModuleCdc = types.ModuleCdc ) type ( + Keeper = keeper.Keeper + Evidence = types.Evidence Handler = types.Handler Router = types.Router diff --git a/x/evidence/internal/keeper/keeper.go b/x/evidence/internal/keeper/keeper.go index 29731385bae8..e40dc248439f 100644 --- a/x/evidence/internal/keeper/keeper.go +++ b/x/evidence/internal/keeper/keeper.go @@ -1,8 +1,13 @@ package keeper import ( + "fmt" + + "github.com/tendermint/tendermint/libs/log" + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/evidence/internal/types" "github.com/cosmos/cosmos-sdk/x/params" ) @@ -13,12 +18,33 @@ type Keeper struct { cdc *codec.Codec storeKey sdk.StoreKey paramSpace params.Subspace + router types.Router + codespace sdk.CodespaceType } -func NewKeeper(cdc *codec.Codec, storeKey sdk.StoreKey, paramSpace params.Subspace) Keeper { +func NewKeeper( + cdc *codec.Codec, storeKey sdk.StoreKey, paramSpace params.Subspace, rtr types.Router, + codespace sdk.CodespaceType, +) Keeper { + return Keeper{ cdc: cdc, storeKey: storeKey, paramSpace: paramSpace, + router: rtr, + codespace: codespace, } } + +// 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)) +} + +func (k Keeper) SubmitEvidence(ctx sdk.Context, evidence types.Evidence) error { + if !k.router.HasRoute(evidence.Route()) { + return types.ErrNoEvidenceHandlerExists(k.codespace, evidence.Route()) + } + + return nil +} diff --git a/x/evidence/internal/types/codec_test.go b/x/evidence/internal/types/codec_test.go index fb4f3e9f1dae..c7042ee785c6 100644 --- a/x/evidence/internal/types/codec_test.go +++ b/x/evidence/internal/types/codec_test.go @@ -7,6 +7,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/evidence/internal/types" "github.com/stretchr/testify/require" + cmn "github.com/tendermint/tendermint/libs/common" ) var _ types.Evidence = (*testEvidence)(nil) @@ -18,6 +19,7 @@ func (te testEvidence) Type() string { return "" } func (te testEvidence) String() string { return "" } func (te testEvidence) ValidateBasic() error { return nil } func (te testEvidence) GetConsensusAddress() sdk.ConsAddress { return nil } +func (te testEvidence) Hash() cmn.HexBytes { return nil } func (te testEvidence) GetHeight() int64 { return 0 } func (te testEvidence) GetValidatorPower() int64 { return 0 } func (te testEvidence) GetTotalPower() int64 { return 0 } From 9e187449086fe73f081852d9a6e9a5159de2eb11 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Fri, 25 Oct 2019 11:44:10 -0400 Subject: [PATCH 21/69] add initial error types --- x/evidence/internal/types/errors.go | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 x/evidence/internal/types/errors.go diff --git a/x/evidence/internal/types/errors.go b/x/evidence/internal/types/errors.go new file mode 100644 index 000000000000..686350320e36 --- /dev/null +++ b/x/evidence/internal/types/errors.go @@ -0,0 +1,25 @@ +package types + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +// Error codes specific to the evidence module +const ( + DefaultCodespace sdk.CodespaceType = ModuleName + + CodeNoEvidenceHandlerExists sdk.CodeType = 1 +) + +// ErrNoEvidenceHandlerExists returns a typed error for an invalid evidence +// handler route. +func ErrNoEvidenceHandlerExists(codespace sdk.CodespaceType, route string) error { + return sdkerrors.New( + string(codespace), + uint32(CodeNoEvidenceHandlerExists), + fmt.Sprintf("route '%s' does not have a registered evidence handler", route), + ) +} From 793b1d4f0b28ebd3428fb48e6717c3a7ea19252c Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Fri, 25 Oct 2019 11:51:16 -0400 Subject: [PATCH 22/69] exec evidence handler --- docs/architecture/adr-009-evidence-module.md | 2 +- x/evidence/internal/keeper/keeper.go | 5 +++++ x/evidence/internal/types/errors.go | 10 ++++++++++ x/evidence/module.go | 5 ++++- 4 files changed, 20 insertions(+), 2 deletions(-) diff --git a/docs/architecture/adr-009-evidence-module.md b/docs/architecture/adr-009-evidence-module.md index 1dda97ac7d5b..0d4edfe5a80f 100644 --- a/docs/architecture/adr-009-evidence-module.md +++ b/docs/architecture/adr-009-evidence-module.md @@ -130,7 +130,7 @@ the module's router and invoking the corresponding `Handler` which may include slashing and jailing the validator. Upon success, the submitted evidence is persisted. ```go -func (k Keeper) SubmitEvidence(ctx Context, evidence Evidence) Error { +func (k Keeper) SubmitEvidence(ctx Context, evidence Evidence) error { handler := keeper.router.GetRoute(evidence.Route()) if err := handler(ctx, evidence); err != nil { return ErrInvalidEvidence(keeper.codespace, err) diff --git a/x/evidence/internal/keeper/keeper.go b/x/evidence/internal/keeper/keeper.go index e40dc248439f..1d550f8b8cde 100644 --- a/x/evidence/internal/keeper/keeper.go +++ b/x/evidence/internal/keeper/keeper.go @@ -46,5 +46,10 @@ func (k Keeper) SubmitEvidence(ctx sdk.Context, evidence types.Evidence) error { return types.ErrNoEvidenceHandlerExists(k.codespace, evidence.Route()) } + handler := k.router.GetRoute(evidence.Route()) + if err := handler(ctx, evidence); err != nil { + return types.ErrInvalidEvidence(k.codespace, err) + } + return nil } diff --git a/x/evidence/internal/types/errors.go b/x/evidence/internal/types/errors.go index 686350320e36..9c6233603d9a 100644 --- a/x/evidence/internal/types/errors.go +++ b/x/evidence/internal/types/errors.go @@ -12,6 +12,7 @@ const ( DefaultCodespace sdk.CodespaceType = ModuleName CodeNoEvidenceHandlerExists sdk.CodeType = 1 + CodeInvalidEvidence sdk.CodeType = 2 ) // ErrNoEvidenceHandlerExists returns a typed error for an invalid evidence @@ -23,3 +24,12 @@ func ErrNoEvidenceHandlerExists(codespace sdk.CodespaceType, route string) error fmt.Sprintf("route '%s' does not have a registered evidence handler", route), ) } + +// ErrInvalidEvidence returns a typed error for invalid evidence. +func ErrInvalidEvidence(codespace sdk.CodespaceType, err error) error { + return sdkerrors.New( + string(codespace), + uint32(CodeInvalidEvidence), + fmt.Sprintf("invalid evidence: %s", err), + ) +} diff --git a/x/evidence/module.go b/x/evidence/module.go index 2202cf65429f..8cbd6dac5006 100644 --- a/x/evidence/module.go +++ b/x/evidence/module.go @@ -94,11 +94,14 @@ func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command { // AppModule implements the AppModule interface for the evidence module. type AppModule struct { AppModuleBasic + + keeper Keeper } -func NewAppModule() AppModule { +func NewAppModule(keeper Keeper) AppModule { return AppModule{ AppModuleBasic: NewAppModuleBasic(), + keeper: keeper, } } From f3dbc52453905342566c7ced869d3236f0309be4 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Fri, 25 Oct 2019 11:52:40 -0400 Subject: [PATCH 23/69] update godoc --- x/evidence/internal/keeper/keeper.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/x/evidence/internal/keeper/keeper.go b/x/evidence/internal/keeper/keeper.go index 1d550f8b8cde..60b5908d5c06 100644 --- a/x/evidence/internal/keeper/keeper.go +++ b/x/evidence/internal/keeper/keeper.go @@ -41,6 +41,10 @@ func (k Keeper) Logger(ctx sdk.Context) log.Logger { return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName)) } +// 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 types.Evidence) error { if !k.router.HasRoute(evidence.Route()) { return types.ErrNoEvidenceHandlerExists(k.codespace, evidence.Route()) From 50c72ef90232be8d2a83681f19889b6f47c8084f Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Fri, 25 Oct 2019 12:01:23 -0400 Subject: [PATCH 24/69] implement and use setEvidence --- x/evidence/internal/keeper/keeper.go | 7 +++++++ x/evidence/internal/types/keys.go | 14 ++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/x/evidence/internal/keeper/keeper.go b/x/evidence/internal/keeper/keeper.go index 60b5908d5c06..8918823d4f77 100644 --- a/x/evidence/internal/keeper/keeper.go +++ b/x/evidence/internal/keeper/keeper.go @@ -55,5 +55,12 @@ func (k Keeper) SubmitEvidence(ctx sdk.Context, evidence types.Evidence) error { return types.ErrInvalidEvidence(k.codespace, err) } + k.setEvidence(ctx, evidence) return nil } + +func (k Keeper) setEvidence(ctx sdk.Context, evidence types.Evidence) { + store := ctx.KVStore(k.storeKey) + bz := k.cdc.MustMarshalBinaryLengthPrefixed(evidence) + store.Set(types.EvidenceKey(evidence.Hash()), bz) +} diff --git a/x/evidence/internal/types/keys.go b/x/evidence/internal/types/keys.go index c8a081a8c4c0..6d32c7258d31 100644 --- a/x/evidence/internal/types/keys.go +++ b/x/evidence/internal/types/keys.go @@ -1,5 +1,9 @@ package types +import ( + cmn "github.com/tendermint/tendermint/libs/common" +) + const ( // ModuleName defines the module name ModuleName = "evidence" @@ -16,3 +20,13 @@ const ( // DefaultParamspace defines the module's default paramspace name DefaultParamspace = ModuleName ) + +// KVStore key prefixes +var ( + KeyPrefixEvidence = []byte{0x00} +) + +// EvidenceKey returns the KVStore key for persisting Evidence. +func EvidenceKey(hash cmn.HexBytes) []byte { + return append(KeyPrefixEvidence, hash...) +} From 79e8e285a6ccf95826938ccfd1fa6e29e595014f Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Sun, 27 Oct 2019 15:26:08 -0400 Subject: [PATCH 25/69] restructure router usage and start keeper test harness --- x/evidence/internal/keeper/common_test.go | 113 ++++++++++++++++++++++ x/evidence/internal/keeper/keeper.go | 41 +++++++- x/evidence/internal/keeper/keeper_test.go | 99 +++++++++++++++++++ x/evidence/internal/types/router.go | 6 ++ 4 files changed, 254 insertions(+), 5 deletions(-) create mode 100644 x/evidence/internal/keeper/common_test.go create mode 100644 x/evidence/internal/keeper/keeper_test.go diff --git a/x/evidence/internal/keeper/common_test.go b/x/evidence/internal/keeper/common_test.go new file mode 100644 index 000000000000..84ce3f7a3ca8 --- /dev/null +++ b/x/evidence/internal/keeper/common_test.go @@ -0,0 +1,113 @@ +package keeper_test + +import ( + "bytes" + "errors" + "fmt" + "time" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/evidence/internal/keeper" + "github.com/cosmos/cosmos-sdk/x/evidence/internal/types" + + "gopkg.in/yaml.v2" + + "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/crypto/tmhash" + cmn "github.com/tendermint/tendermint/libs/common" +) + +var ( + _ types.Evidence = (*EquivocationEvidence)(nil) + cdc = codec.New() +) + +const ( + EvidenceRouteEquivocation = "EquivocationEvidence" + EvidenceTypeEquivocation = "equivocation" +) + +type ( + SimpleVote struct { + Height int64 + Round int64 + Timestamp time.Time + ValidatorAddress cmn.HexBytes + Signature []byte + } + + EquivocationEvidence struct { + Power int64 + TotalPower int64 + PubKey crypto.PubKey + VoteA SimpleVote + VoteB SimpleVote + } +) + +func init() { + types.RegisterCodec(cdc) + codec.RegisterCrypto(cdc) + types.RegisterEvidenceTypeCodec(EquivocationEvidence{}, "test/EquivocationEvidence") +} + +func (e EquivocationEvidence) Route() string { return EvidenceRouteEquivocation } +func (e EquivocationEvidence) Type() string { return EvidenceTypeEquivocation } +func (e EquivocationEvidence) GetHeight() int64 { return e.VoteA.Height } +func (e EquivocationEvidence) GetValidatorPower() int64 { return e.Power } +func (e EquivocationEvidence) GetTotalPower() int64 { return e.TotalPower } + +func (e EquivocationEvidence) String() string { + bz, _ := yaml.Marshal(e) + return string(bz) +} + +func (e EquivocationEvidence) GetConsensusAddress() sdk.ConsAddress { + return sdk.ConsAddress(e.PubKey.Address()) +} + +func (e EquivocationEvidence) ValidateBasic() error { + if e.VoteA.Height != e.VoteB.Height || + e.VoteA.Round != e.VoteB.Round { + return fmt.Errorf("invalid evidence; H/R/S does not match (got %v and %v)", e.VoteA, e.VoteB) + } + + if !bytes.Equal(e.VoteA.ValidatorAddress, e.VoteB.ValidatorAddress) { + return fmt.Errorf( + "invalid evidence; validator addresses do not match (got %X and %X)", + e.VoteA.ValidatorAddress, + e.VoteB.ValidatorAddress, + ) + } + + if !e.PubKey.VerifyBytes(e.VoteA.SignBytes(), e.VoteA.Signature) { + return errors.New("invalid evidence; failed to verify vote A signature") + } + if !e.PubKey.VerifyBytes(e.VoteB.SignBytes(), e.VoteB.Signature) { + return errors.New("invalid evidence; failed to verify vote B signature") + } + + return nil +} + +func (e EquivocationEvidence) Hash() cmn.HexBytes { + return tmhash.Sum(cdc.MustMarshalBinaryBare(e)) +} + +func (v SimpleVote) SignBytes() []byte { + bz, _ := cdc.MarshalBinaryLengthPrefixed(v) + return bz +} + +func EquivocationHandler(k keeper.Keeper) types.Handler { + return func(ctx sdk.Context, e types.Evidence) error { + if err := e.ValidateBasic(); err != nil { + return err + } + + // TODO: Slashing! + + return nil + } +} diff --git a/x/evidence/internal/keeper/keeper.go b/x/evidence/internal/keeper/keeper.go index 8918823d4f77..44aaef1e5e8a 100644 --- a/x/evidence/internal/keeper/keeper.go +++ b/x/evidence/internal/keeper/keeper.go @@ -3,6 +3,7 @@ package keeper import ( "fmt" + cmn "github.com/tendermint/tendermint/libs/common" "github.com/tendermint/tendermint/libs/log" "github.com/cosmos/cosmos-sdk/codec" @@ -23,15 +24,13 @@ type Keeper struct { } func NewKeeper( - cdc *codec.Codec, storeKey sdk.StoreKey, paramSpace params.Subspace, rtr types.Router, - codespace sdk.CodespaceType, -) Keeper { + cdc *codec.Codec, storeKey sdk.StoreKey, paramSpace params.Subspace, codespace sdk.CodespaceType, +) *Keeper { - return Keeper{ + return &Keeper{ cdc: cdc, storeKey: storeKey, paramSpace: paramSpace, - router: rtr, codespace: codespace, } } @@ -41,6 +40,24 @@ 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 +} + // 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 @@ -64,3 +81,17 @@ func (k Keeper) setEvidence(ctx sdk.Context, evidence types.Evidence) { bz := k.cdc.MustMarshalBinaryLengthPrefixed(evidence) store.Set(types.EvidenceKey(evidence.Hash()), bz) } + +// 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 cmn.HexBytes) (evidence types.Evidence, found bool) { + store := ctx.KVStore(k.storeKey) + + bz := store.Get(types.EvidenceKey(hash)) + if len(bz) == 0 { + return nil, false + } + + k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &evidence) + return evidence, true +} diff --git a/x/evidence/internal/keeper/keeper_test.go b/x/evidence/internal/keeper/keeper_test.go new file mode 100644 index 000000000000..f6afee78a2f2 --- /dev/null +++ b/x/evidence/internal/keeper/keeper_test.go @@ -0,0 +1,99 @@ +package keeper_test + +import ( + "testing" + + "github.com/cosmos/cosmos-sdk/store" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/evidence/internal/keeper" + "github.com/cosmos/cosmos-sdk/x/evidence/internal/types" + "github.com/cosmos/cosmos-sdk/x/params" + + "github.com/stretchr/testify/suite" + abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/crypto/ed25519" + "github.com/tendermint/tendermint/libs/log" + tmtypes "github.com/tendermint/tendermint/types" + dbm "github.com/tendermint/tm-db" +) + +type KeeperTestSuite struct { + suite.Suite + + cms sdk.CommitMultiStore + ctx sdk.Context + keeper *keeper.Keeper +} + +func (suite *KeeperTestSuite) SetupTest() { + // create required store keys + keyParams := sdk.NewKVStoreKey(params.StoreKey) + tkeyParams := sdk.NewTransientStoreKey(params.TStoreKey) + storeKey := sdk.NewKVStoreKey(types.StoreKey) + + // create required keepers + paramsKeeper := params.NewKeeper(cdc, keyParams, tkeyParams, params.DefaultCodespace) + subspace := paramsKeeper.Subspace(types.DefaultParamspace) + keeper := keeper.NewKeeper(cdc, storeKey, subspace, types.DefaultCodespace) + + // create Evidence router, mount Handlers, and set keeper's router + router := types.NewRouter() + router = router.AddRoute(EvidenceRouteEquivocation, EquivocationHandler(*keeper)) + keeper.SetRouter(router) + + // create DB, mount stores, and load latest version + db := dbm.NewMemDB() + cms := store.NewCommitMultiStore(db) + cms.MountStoreWithDB(keyParams, sdk.StoreTypeIAVL, db) + cms.MountStoreWithDB(tkeyParams, sdk.StoreTypeTransient, db) + cms.MountStoreWithDB(storeKey, sdk.StoreTypeIAVL, db) + suite.Nil(cms.LoadLatestVersion()) + + // create initial Context + ctx := sdk.NewContext(cms, abci.Header{ChainID: "test-chain"}, false, log.NewNopLogger()) + ctx = ctx.WithConsensusParams( + &abci.ConsensusParams{ + Validator: &abci.ValidatorParams{ + PubKeyTypes: []string{tmtypes.ABCIPubKeyTypeEd25519}, + }, + }, + ) + + suite.cms = cms + suite.ctx = ctx + suite.keeper = keeper +} + +// func (suite *KeeperTestSuite) TestSubmitValidEvidence(t *testing.T) { + +// } + +func (suite *KeeperTestSuite) TestSubmitInvalidEvidence() { + ctx := suite.ctx.WithIsCheckTx(false) + pk := ed25519.GenPrivKey() + e := EquivocationEvidence{ + Power: 100, + TotalPower: 100000, + PubKey: pk.PubKey(), + VoteA: SimpleVote{ + ValidatorAddress: pk.PubKey().Address(), + Height: 11, + Round: 0, + }, + VoteB: SimpleVote{ + ValidatorAddress: pk.PubKey().Address(), + Height: 11, + Round: 0, + }, + } + + suite.Error(suite.keeper.SubmitEvidence(ctx, e)) + + res, ok := suite.keeper.GetEvidence(ctx, e.Hash()) + suite.False(ok) + suite.Nil(res) +} + +func TestKeeperTestSuite(t *testing.T) { + suite.Run(t, new(KeeperTestSuite)) +} diff --git a/x/evidence/internal/types/router.go b/x/evidence/internal/types/router.go index 5389353e36cc..2d99a0f0a5d3 100644 --- a/x/evidence/internal/types/router.go +++ b/x/evidence/internal/types/router.go @@ -20,6 +20,7 @@ type ( HasRoute(r string) bool GetRoute(path string) Handler Seal() + Sealed() bool } router struct { @@ -43,6 +44,11 @@ func (rtr *router) Seal() { rtr.sealed = true } +// Sealed returns a boolean signifying if the Router is sealed or not. +func (rtr router) Sealed() bool { + return rtr.sealed +} + // AddRoute adds a governance handler for a given path. It returns the Router // so AddRoute calls can be linked. It will panic if the router is sealed. func (rtr *router) AddRoute(path string, h Handler) Router { From e1c01205734028627ef82ff3b43affba524fad23 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Sun, 27 Oct 2019 17:32:05 -0400 Subject: [PATCH 26/69] add tests --- x/evidence/internal/keeper/common_test.go | 41 ++++++++++++++++------- x/evidence/internal/keeper/keeper_test.go | 30 +++++++++++++++-- 2 files changed, 56 insertions(+), 15 deletions(-) diff --git a/x/evidence/internal/keeper/common_test.go b/x/evidence/internal/keeper/common_test.go index 84ce3f7a3ca8..5e19db726fcc 100644 --- a/x/evidence/internal/keeper/common_test.go +++ b/x/evidence/internal/keeper/common_test.go @@ -37,6 +37,13 @@ type ( Signature []byte } + SimpleCanonicalVote struct { + Height int64 + Round int64 + Timestamp time.Time + ChainID string + } + EquivocationEvidence struct { Power int64 TotalPower int64 @@ -49,7 +56,7 @@ type ( func init() { types.RegisterCodec(cdc) codec.RegisterCrypto(cdc) - types.RegisterEvidenceTypeCodec(EquivocationEvidence{}, "test/EquivocationEvidence") + cdc.RegisterConcrete(EquivocationEvidence{}, "test/EquivocationEvidence", nil) } func (e EquivocationEvidence) Route() string { return EvidenceRouteEquivocation } @@ -70,24 +77,17 @@ func (e EquivocationEvidence) GetConsensusAddress() sdk.ConsAddress { func (e EquivocationEvidence) ValidateBasic() error { if e.VoteA.Height != e.VoteB.Height || e.VoteA.Round != e.VoteB.Round { - return fmt.Errorf("invalid evidence; H/R/S does not match (got %v and %v)", e.VoteA, e.VoteB) + return fmt.Errorf("H/R/S does not match (got %v and %v)", e.VoteA, e.VoteB) } if !bytes.Equal(e.VoteA.ValidatorAddress, e.VoteB.ValidatorAddress) { return fmt.Errorf( - "invalid evidence; validator addresses do not match (got %X and %X)", + "validator addresses do not match (got %X and %X)", e.VoteA.ValidatorAddress, e.VoteB.ValidatorAddress, ) } - if !e.PubKey.VerifyBytes(e.VoteA.SignBytes(), e.VoteA.Signature) { - return errors.New("invalid evidence; failed to verify vote A signature") - } - if !e.PubKey.VerifyBytes(e.VoteB.SignBytes(), e.VoteB.Signature) { - return errors.New("invalid evidence; failed to verify vote B signature") - } - return nil } @@ -95,8 +95,14 @@ func (e EquivocationEvidence) Hash() cmn.HexBytes { return tmhash.Sum(cdc.MustMarshalBinaryBare(e)) } -func (v SimpleVote) SignBytes() []byte { - bz, _ := cdc.MarshalBinaryLengthPrefixed(v) +func (v SimpleVote) SignBytes(chainID string) []byte { + scv := SimpleCanonicalVote{ + Height: v.Height, + Round: v.Round, + Timestamp: v.Timestamp, + ChainID: chainID, + } + bz, _ := cdc.MarshalBinaryLengthPrefixed(scv) return bz } @@ -106,6 +112,17 @@ func EquivocationHandler(k keeper.Keeper) types.Handler { return err } + ee, ok := e.(EquivocationEvidence) + if !ok { + return fmt.Errorf("unexpected evidence type: %T", e) + } + if !ee.PubKey.VerifyBytes(ee.VoteA.SignBytes(ctx.ChainID()), ee.VoteA.Signature) { + return errors.New("failed to verify vote A signature") + } + if !ee.PubKey.VerifyBytes(ee.VoteB.SignBytes(ctx.ChainID()), ee.VoteB.Signature) { + return errors.New("failed to verify vote B signature") + } + // TODO: Slashing! return nil diff --git a/x/evidence/internal/keeper/keeper_test.go b/x/evidence/internal/keeper/keeper_test.go index f6afee78a2f2..0eb7ae0eeef5 100644 --- a/x/evidence/internal/keeper/keeper_test.go +++ b/x/evidence/internal/keeper/keeper_test.go @@ -64,9 +64,33 @@ func (suite *KeeperTestSuite) SetupTest() { suite.keeper = keeper } -// func (suite *KeeperTestSuite) TestSubmitValidEvidence(t *testing.T) { +func (suite *KeeperTestSuite) TestSubmitValidEvidence() { + ctx := suite.ctx.WithIsCheckTx(false) + pk := ed25519.GenPrivKey() + sv := SimpleVote{ + ValidatorAddress: pk.PubKey().Address(), + Height: 11, + Round: 0, + } + + sig, err := pk.Sign(sv.SignBytes(ctx.ChainID())) + suite.NoError(err) + sv.Signature = sig + + e := EquivocationEvidence{ + Power: 100, + TotalPower: 100000, + PubKey: pk.PubKey(), + VoteA: sv, + VoteB: sv, + } -// } + suite.Nil(suite.keeper.SubmitEvidence(ctx, e)) + + res, ok := suite.keeper.GetEvidence(ctx, e.Hash()) + suite.True(ok) + suite.Equal(e, res) +} func (suite *KeeperTestSuite) TestSubmitInvalidEvidence() { ctx := suite.ctx.WithIsCheckTx(false) @@ -77,7 +101,7 @@ func (suite *KeeperTestSuite) TestSubmitInvalidEvidence() { PubKey: pk.PubKey(), VoteA: SimpleVote{ ValidatorAddress: pk.PubKey().Address(), - Height: 11, + Height: 10, Round: 0, }, VoteB: SimpleVote{ From ce64ee60a0b62ed32c850744f0e0ad5f493e3f62 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Mon, 28 Oct 2019 09:46:51 -0400 Subject: [PATCH 27/69] use prefix stores instead of key comp. --- x/evidence/internal/keeper/keeper.go | 9 +++++---- x/evidence/internal/types/keys.go | 9 --------- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/x/evidence/internal/keeper/keeper.go b/x/evidence/internal/keeper/keeper.go index 44aaef1e5e8a..07bf00415a77 100644 --- a/x/evidence/internal/keeper/keeper.go +++ b/x/evidence/internal/keeper/keeper.go @@ -7,6 +7,7 @@ import ( "github.com/tendermint/tendermint/libs/log" "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/evidence/internal/types" "github.com/cosmos/cosmos-sdk/x/params" @@ -77,17 +78,17 @@ func (k Keeper) SubmitEvidence(ctx sdk.Context, evidence types.Evidence) error { } func (k Keeper) setEvidence(ctx sdk.Context, evidence types.Evidence) { - store := ctx.KVStore(k.storeKey) + store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixEvidence) bz := k.cdc.MustMarshalBinaryLengthPrefixed(evidence) - store.Set(types.EvidenceKey(evidence.Hash()), bz) + store.Set(evidence.Hash(), bz) } // 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 cmn.HexBytes) (evidence types.Evidence, found bool) { - store := ctx.KVStore(k.storeKey) + store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixEvidence) - bz := store.Get(types.EvidenceKey(hash)) + bz := store.Get(hash) if len(bz) == 0 { return nil, false } diff --git a/x/evidence/internal/types/keys.go b/x/evidence/internal/types/keys.go index 6d32c7258d31..702f76d9b64a 100644 --- a/x/evidence/internal/types/keys.go +++ b/x/evidence/internal/types/keys.go @@ -1,9 +1,5 @@ package types -import ( - cmn "github.com/tendermint/tendermint/libs/common" -) - const ( // ModuleName defines the module name ModuleName = "evidence" @@ -25,8 +21,3 @@ const ( var ( KeyPrefixEvidence = []byte{0x00} ) - -// EvidenceKey returns the KVStore key for persisting Evidence. -func EvidenceKey(hash cmn.HexBytes) []byte { - return append(KeyPrefixEvidence, hash...) -} From 10537e36b7c1544e81ba0e336a829f2949cfd695 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Mon, 28 Oct 2019 10:14:41 -0400 Subject: [PATCH 28/69] implement iterator with tests --- x/evidence/internal/keeper/keeper.go | 27 ++++++++++++++++++++ x/evidence/internal/keeper/keeper_test.go | 31 +++++++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/x/evidence/internal/keeper/keeper.go b/x/evidence/internal/keeper/keeper.go index 07bf00415a77..c1791c63648b 100644 --- a/x/evidence/internal/keeper/keeper.go +++ b/x/evidence/internal/keeper/keeper.go @@ -96,3 +96,30 @@ func (k Keeper) GetEvidence(ctx sdk.Context, hash cmn.HexBytes) (evidence types. k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &evidence) return evidence, 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(types.Evidence) bool) { + store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixEvidence) + iterator := sdk.KVStorePrefixIterator(store, nil) + + defer iterator.Close() + for ; iterator.Valid(); iterator.Next() { + var evidence types.Evidence + k.cdc.MustUnmarshalBinaryLengthPrefixed(iterator.Value(), &evidence) + + if cb(evidence) { + break + } + } +} + +// GetAllEvidence returns all stored Evidence objects. +func (k Keeper) GetAllEvidence(ctx sdk.Context) (evidence []types.Evidence) { + k.IterateEvidence(ctx, func(e types.Evidence) bool { + evidence = append(evidence, e) + return false + }) + return evidence +} diff --git a/x/evidence/internal/keeper/keeper_test.go b/x/evidence/internal/keeper/keeper_test.go index 0eb7ae0eeef5..f8be05875ec9 100644 --- a/x/evidence/internal/keeper/keeper_test.go +++ b/x/evidence/internal/keeper/keeper_test.go @@ -118,6 +118,37 @@ func (suite *KeeperTestSuite) TestSubmitInvalidEvidence() { suite.Nil(res) } +func (suite *KeeperTestSuite) TestIterateEvidence() { + ctx := suite.ctx.WithIsCheckTx(false) + numEvidence := 100 + + for i := 0; i < numEvidence; i++ { + pk := ed25519.GenPrivKey() + sv := SimpleVote{ + ValidatorAddress: pk.PubKey().Address(), + Height: int64(i), + Round: 0, + } + + sig, err := pk.Sign(sv.SignBytes(ctx.ChainID())) + suite.NoError(err) + sv.Signature = sig + + e := EquivocationEvidence{ + Power: 100, + TotalPower: 100000, + PubKey: pk.PubKey(), + VoteA: sv, + VoteB: sv, + } + + suite.Nil(suite.keeper.SubmitEvidence(ctx, e)) + } + + evidence := suite.keeper.GetAllEvidence(ctx) + suite.Len(evidence, numEvidence) +} + func TestKeeperTestSuite(t *testing.T) { suite.Run(t, new(KeeperTestSuite)) } From 39d11420ea793c3bf7957c99b9c7dbd04e27ca8e Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Mon, 28 Oct 2019 13:32:55 -0400 Subject: [PATCH 29/69] add new error types with converter --- types/errors.go | 16 ++++++++++++++++ types/errors/errors.go | 6 ++++++ 2 files changed, 22 insertions(+) diff --git a/types/errors.go b/types/errors.go index 1447a21b4cd3..c59571bfe540 100644 --- a/types/errors.go +++ b/types/errors.go @@ -309,6 +309,22 @@ func ResultFromError(err error) Result { } } +// ConvertError accepts a standard error and attempts to convert it to an sdk.Error. +// If the given error is already an sdk.Error, it'll simply be returned. Otherwise, +// it'll convert it to a types.Error. This is meant to provide a migration path +// away from sdk.Error in favor of types.Error. +func ConvertError(err error) Error { + if err == nil { + return nil + } + if sdkError, ok := err.(Error); ok { + return sdkError + } + + space, code, log := sdkerrors.ABCIInfo(err, false) + return NewError(CodespaceType(space), CodeType(code), log) +} + //---------------------------------------- // REST error utilities diff --git a/types/errors/errors.go b/types/errors/errors.go index af4529f6b0f0..22ed2f4c8c89 100644 --- a/types/errors/errors.go +++ b/types/errors/errors.go @@ -65,6 +65,12 @@ var ( // ErrNoSignatures to doc ErrNoSignatures = Register(RootCodespace, 16, "no signatures supplied") + // ErrJSONMarshal defines an ABCI typed JSON marshalling error + ErrJSONMarshal = Register(RootCodespace, 17, "failed to marshal JSON bytes") + + // ErrJSONUnmarshal defines an ABCI typed JSON unmarshalling error + ErrJSONUnmarshal = Register(RootCodespace, 18, "failed to unmarshal JSON bytes") + // ErrPanic is only set when we recover from a panic, so we know to // redact potentially sensitive system info ErrPanic = Register(UndefinedCodespace, 111222, "panic") From e63830670eb92d558b667ab4ec5ba7386043df62 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Mon, 28 Oct 2019 13:33:13 -0400 Subject: [PATCH 30/69] start querier types --- x/evidence/internal/types/querier.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 x/evidence/internal/types/querier.go diff --git a/x/evidence/internal/types/querier.go b/x/evidence/internal/types/querier.go new file mode 100644 index 000000000000..cebb68a744dc --- /dev/null +++ b/x/evidence/internal/types/querier.go @@ -0,0 +1,16 @@ +package types + +// Querier routes for the evidence module +const ( + QueryEvidence = "evidence" + QueryAllEvidence = "all_evidence" +) + +// QueryEvidenceParams defines the parameters necessary for querying Evidence. +type QueryEvidenceParams struct { + EvidenceHash string +} + +func NewQueryEvidenceParams(hash string) QueryEvidenceParams { + return QueryEvidenceParams{EvidenceHash: hash} +} From a593d67b5683d27cfad113b9395dab062b0f6b4c Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Mon, 28 Oct 2019 13:33:36 -0400 Subject: [PATCH 31/69] add ErrNoEvidenceExists --- x/evidence/internal/types/errors.go | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/x/evidence/internal/types/errors.go b/x/evidence/internal/types/errors.go index 9c6233603d9a..72870685b340 100644 --- a/x/evidence/internal/types/errors.go +++ b/x/evidence/internal/types/errors.go @@ -13,9 +13,10 @@ const ( CodeNoEvidenceHandlerExists sdk.CodeType = 1 CodeInvalidEvidence sdk.CodeType = 2 + CodeNoEvidenceExists sdk.CodeType = 3 ) -// ErrNoEvidenceHandlerExists returns a typed error for an invalid evidence +// ErrNoEvidenceHandlerExists returns a typed ABCI error for an invalid evidence // handler route. func ErrNoEvidenceHandlerExists(codespace sdk.CodespaceType, route string) error { return sdkerrors.New( @@ -25,7 +26,7 @@ func ErrNoEvidenceHandlerExists(codespace sdk.CodespaceType, route string) error ) } -// ErrInvalidEvidence returns a typed error for invalid evidence. +// ErrInvalidEvidence returns a typed ABCI error for invalid evidence. func ErrInvalidEvidence(codespace sdk.CodespaceType, err error) error { return sdkerrors.New( string(codespace), @@ -33,3 +34,13 @@ func ErrInvalidEvidence(codespace sdk.CodespaceType, err error) error { fmt.Sprintf("invalid evidence: %s", err), ) } + +// ErrNoEvidenceExists returns a typed ABCI error for Evidence that does not exist +// for a given hash. +func ErrNoEvidenceExists(codespace sdk.CodespaceType, hash string) error { + return sdkerrors.New( + string(codespace), + uint32(CodeNoEvidenceExists), + fmt.Sprintf("evidence with hash %s does not exist", hash), + ) +} From e90488f2c0f2d128173e9069ce289def473fe4af Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Mon, 28 Oct 2019 14:10:22 -0400 Subject: [PATCH 32/69] implement querier with tests --- x/evidence/internal/keeper/keeper_test.go | 69 +++++++++-------- x/evidence/internal/keeper/querier.go | 86 ++++++++++++++++++++++ x/evidence/internal/keeper/querier_test.go | 85 +++++++++++++++++++++ x/evidence/internal/types/querier.go | 9 +++ 4 files changed, 219 insertions(+), 30 deletions(-) create mode 100644 x/evidence/internal/keeper/querier.go create mode 100644 x/evidence/internal/keeper/querier_test.go diff --git a/x/evidence/internal/keeper/keeper_test.go b/x/evidence/internal/keeper/keeper_test.go index f8be05875ec9..bd9c7b84a731 100644 --- a/x/evidence/internal/keeper/keeper_test.go +++ b/x/evidence/internal/keeper/keeper_test.go @@ -20,9 +20,10 @@ import ( type KeeperTestSuite struct { suite.Suite - cms sdk.CommitMultiStore - ctx sdk.Context - keeper *keeper.Keeper + cms sdk.CommitMultiStore + ctx sdk.Context + querier sdk.Querier + keeper *keeper.Keeper } func (suite *KeeperTestSuite) SetupTest() { @@ -34,12 +35,12 @@ func (suite *KeeperTestSuite) SetupTest() { // create required keepers paramsKeeper := params.NewKeeper(cdc, keyParams, tkeyParams, params.DefaultCodespace) subspace := paramsKeeper.Subspace(types.DefaultParamspace) - keeper := keeper.NewKeeper(cdc, storeKey, subspace, types.DefaultCodespace) + evidenceKeeper := keeper.NewKeeper(cdc, storeKey, subspace, types.DefaultCodespace) // create Evidence router, mount Handlers, and set keeper's router router := types.NewRouter() - router = router.AddRoute(EvidenceRouteEquivocation, EquivocationHandler(*keeper)) - keeper.SetRouter(router) + router = router.AddRoute(EvidenceRouteEquivocation, EquivocationHandler(*evidenceKeeper)) + evidenceKeeper.SetRouter(router) // create DB, mount stores, and load latest version db := dbm.NewMemDB() @@ -61,7 +62,37 @@ func (suite *KeeperTestSuite) SetupTest() { suite.cms = cms suite.ctx = ctx - suite.keeper = keeper + suite.querier = keeper.NewQuerier(*evidenceKeeper) + suite.keeper = evidenceKeeper +} + +func (suite *KeeperTestSuite) populateEvidence(ctx sdk.Context, numEvidence int) []types.Evidence { + evidence := make([]types.Evidence, numEvidence) + + for i := 0; i < numEvidence; i++ { + pk := ed25519.GenPrivKey() + sv := SimpleVote{ + ValidatorAddress: pk.PubKey().Address(), + Height: int64(i), + Round: 0, + } + + sig, err := pk.Sign(sv.SignBytes(ctx.ChainID())) + suite.NoError(err) + sv.Signature = sig + + evidence[i] = EquivocationEvidence{ + Power: 100, + TotalPower: 100000, + PubKey: pk.PubKey(), + VoteA: sv, + VoteB: sv, + } + + suite.Nil(suite.keeper.SubmitEvidence(ctx, evidence[i])) + } + + return evidence } func (suite *KeeperTestSuite) TestSubmitValidEvidence() { @@ -121,29 +152,7 @@ func (suite *KeeperTestSuite) TestSubmitInvalidEvidence() { func (suite *KeeperTestSuite) TestIterateEvidence() { ctx := suite.ctx.WithIsCheckTx(false) numEvidence := 100 - - for i := 0; i < numEvidence; i++ { - pk := ed25519.GenPrivKey() - sv := SimpleVote{ - ValidatorAddress: pk.PubKey().Address(), - Height: int64(i), - Round: 0, - } - - sig, err := pk.Sign(sv.SignBytes(ctx.ChainID())) - suite.NoError(err) - sv.Signature = sig - - e := EquivocationEvidence{ - Power: 100, - TotalPower: 100000, - PubKey: pk.PubKey(), - VoteA: sv, - VoteB: sv, - } - - suite.Nil(suite.keeper.SubmitEvidence(ctx, e)) - } + suite.populateEvidence(ctx, numEvidence) evidence := suite.keeper.GetAllEvidence(ctx) suite.Len(evidence, numEvidence) diff --git a/x/evidence/internal/keeper/querier.go b/x/evidence/internal/keeper/querier.go new file mode 100644 index 000000000000..97d205678a0b --- /dev/null +++ b/x/evidence/internal/keeper/querier.go @@ -0,0 +1,86 @@ +package keeper + +import ( + "encoding/hex" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/x/evidence/internal/types" + + abci "github.com/tendermint/tendermint/abci/types" +) + +func NewQuerier(k Keeper) sdk.Querier { + return func(ctx sdk.Context, path []string, req abci.RequestQuery) ([]byte, sdk.Error) { + var ( + res []byte + err error + ) + + switch path[0] { + case types.QueryEvidence: + res, err = queryEvidence(ctx, path[1:], req, k) + + case types.QueryAllEvidence: + res, err = queryAllEvidence(ctx, path[1:], req, k) + + default: + err = sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unknown %s query endpoint", types.ModuleName) + } + + return res, sdk.ConvertError(err) + } +} + +func queryEvidence(ctx sdk.Context, _ []string, req abci.RequestQuery, k Keeper) ([]byte, error) { + var params types.QueryEvidenceParams + + err := k.cdc.UnmarshalJSON(req.Data, ¶ms) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) + } + + hash, err := hex.DecodeString(params.EvidenceHash) + if err != nil { + return nil, sdkerrors.Wrap(err, "failed to decode evidence hash string query") + } + + evidence, ok := k.GetEvidence(ctx, hash) + if !ok { + return nil, types.ErrNoEvidenceExists(k.codespace, params.EvidenceHash) + } + + res, err := codec.MarshalJSONIndent(k.cdc, evidence) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) + } + + return res, nil +} + +func queryAllEvidence(ctx sdk.Context, _ []string, req abci.RequestQuery, k Keeper) ([]byte, error) { + var params types.QueryAllEvidenceParams + + err := k.cdc.UnmarshalJSON(req.Data, ¶ms) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) + } + + evidence := k.GetAllEvidence(ctx) + + start, end := client.Paginate(len(evidence), params.Page, params.Limit, 100) + if start < 0 || end < 0 { + evidence = []types.Evidence{} + } else { + evidence = evidence[start:end] + } + + res, err := codec.MarshalJSONIndent(k.cdc, evidence) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) + } + + return res, nil +} diff --git a/x/evidence/internal/keeper/querier_test.go b/x/evidence/internal/keeper/querier_test.go new file mode 100644 index 000000000000..bce8f35291a4 --- /dev/null +++ b/x/evidence/internal/keeper/querier_test.go @@ -0,0 +1,85 @@ +package keeper_test + +import ( + "strings" + + "github.com/cosmos/cosmos-sdk/x/evidence/internal/types" + + abci "github.com/tendermint/tendermint/abci/types" +) + +const ( + custom = "custom" +) + +func (suite *KeeperTestSuite) TestQueryEvidence_Existing() { + ctx := suite.ctx.WithIsCheckTx(false) + numEvidence := 100 + + evidence := suite.populateEvidence(ctx, numEvidence) + query := abci.RequestQuery{ + Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryEvidence}, "/"), + Data: cdc.MustMarshalJSON(types.NewQueryEvidenceParams(evidence[0].Hash().String())), + } + + bz, err := suite.querier(ctx, []string{types.QueryEvidence}, query) + suite.Nil(err) + suite.NotNil(bz) + + var e types.Evidence + suite.Nil(cdc.UnmarshalJSON(bz, &e)) + suite.Equal(evidence[0], e) +} + +func (suite *KeeperTestSuite) TestQueryEvidence_NonExisting() { + ctx := suite.ctx.WithIsCheckTx(false) + numEvidence := 100 + + suite.populateEvidence(ctx, numEvidence) + query := abci.RequestQuery{ + Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryEvidence}, "/"), + Data: cdc.MustMarshalJSON(types.NewQueryEvidenceParams("0000000000000000000000000000000000000000000000000000000000000000")), + } + + bz, err := suite.querier(ctx, []string{types.QueryEvidence}, query) + suite.NotNil(err) + suite.Nil(bz) +} + +func (suite *KeeperTestSuite) TestQueryAllEvidence() { + ctx := suite.ctx.WithIsCheckTx(false) + numEvidence := 100 + + suite.populateEvidence(ctx, numEvidence) + query := abci.RequestQuery{ + Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryAllEvidence}, "/"), + Data: cdc.MustMarshalJSON(types.NewQueryAllEvidenceParams(1, numEvidence)), + } + + bz, err := suite.querier(ctx, []string{types.QueryAllEvidence}, query) + suite.Nil(err) + suite.NotNil(bz) + + var e []types.Evidence + suite.Nil(cdc.UnmarshalJSON(bz, &e)) + suite.Len(e, numEvidence) +} + +func (suite *KeeperTestSuite) TestQueryAllEvidence_InvalidPagination() { + ctx := suite.ctx.WithIsCheckTx(false) + numEvidence := 100 + + suite.populateEvidence(ctx, numEvidence) + query := abci.RequestQuery{ + Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryAllEvidence}, "/"), + Data: cdc.MustMarshalJSON(types.NewQueryAllEvidenceParams(0, numEvidence)), + } + + bz, err := suite.querier(ctx, []string{types.QueryAllEvidence}, query) + suite.Nil(err) + suite.NotNil(bz) + + var e []types.Evidence + suite.Nil(cdc.UnmarshalJSON(bz, &e)) + suite.Len(e, 0) +} diff --git a/x/evidence/internal/types/querier.go b/x/evidence/internal/types/querier.go index cebb68a744dc..9e69df28cb2c 100644 --- a/x/evidence/internal/types/querier.go +++ b/x/evidence/internal/types/querier.go @@ -14,3 +14,12 @@ type QueryEvidenceParams struct { func NewQueryEvidenceParams(hash string) QueryEvidenceParams { return QueryEvidenceParams{EvidenceHash: hash} } + +type QueryAllEvidenceParams struct { + Page int + Limit int +} + +func NewQueryAllEvidenceParams(page, limit int) QueryAllEvidenceParams { + return QueryAllEvidenceParams{Page: page, Limit: limit} +} From 674fc90f9c8dd63e84d5b6e1893b73a644b3c71b Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Mon, 28 Oct 2019 14:13:41 -0400 Subject: [PATCH 33/69] implement NewQuerierHandler and update alias' --- x/evidence/alias.go | 20 ++++++++++++++------ x/evidence/module.go | 3 +-- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/x/evidence/alias.go b/x/evidence/alias.go index 9533b2f3c206..d516a9bdf697 100644 --- a/x/evidence/alias.go +++ b/x/evidence/alias.go @@ -8,16 +8,24 @@ import ( // nolint const ( - ModuleName = types.ModuleName - StoreKey = types.StoreKey - RouterKey = types.RouterKey - QuerierRoute = types.QuerierRoute - DefaultParamspace = types.DefaultParamspace + ModuleName = types.ModuleName + StoreKey = types.StoreKey + RouterKey = types.RouterKey + QuerierRoute = types.QuerierRoute + DefaultParamspace = types.DefaultParamspace + QueryEvidence = types.QueryEvidence + QueryAllEvidence = types.QueryAllEvidence + CodeNoEvidenceHandlerExists = types.CodeNoEvidenceHandlerExists + CodeInvalidEvidence = types.CodeInvalidEvidence + CodeNoEvidenceExists = types.CodeNoEvidenceExists ) var ( - NewKeeper = keeper.NewKeeper + NewKeeper = keeper.NewKeeper + NewQuerier = keeper.NewQuerier + NewQueryEvidenceParams = types.NewQueryEvidenceParams + NewQueryAllEvidenceParams = types.NewQueryAllEvidenceParams RegisterCodec = types.RegisterCodec RegisterEvidenceTypeCodec = types.RegisterEvidenceTypeCodec ModuleCdc = types.ModuleCdc diff --git a/x/evidence/module.go b/x/evidence/module.go index 8cbd6dac5006..31cfb8b10422 100644 --- a/x/evidence/module.go +++ b/x/evidence/module.go @@ -133,8 +133,7 @@ func (am AppModule) NewHandler() sdk.Handler { // NewQuerierHandler returns the evidence module's Querier. func (am AppModule) NewQuerierHandler() sdk.Querier { - // TODO: Construct and return Querier. - return nil + return NewQuerier(am.keeper) } // InitGenesis performs the evidence module's genesis initialization It returns From 9d4c1eaca176ad7982e5f13c24f0c3c417d4797a Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Mon, 28 Oct 2019 14:34:18 -0400 Subject: [PATCH 34/69] godoc and misc. --- x/evidence/internal/types/querier.go | 1 + x/evidence/module.go | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/x/evidence/internal/types/querier.go b/x/evidence/internal/types/querier.go index 9e69df28cb2c..1745884bb8b3 100644 --- a/x/evidence/internal/types/querier.go +++ b/x/evidence/internal/types/querier.go @@ -15,6 +15,7 @@ func NewQueryEvidenceParams(hash string) QueryEvidenceParams { return QueryEvidenceParams{EvidenceHash: hash} } +// QueryAllEvidenceParams defines the parameters necessary for querying for all Evidence. type QueryAllEvidenceParams struct { Page int Limit int diff --git a/x/evidence/module.go b/x/evidence/module.go index 31cfb8b10422..655de988337c 100644 --- a/x/evidence/module.go +++ b/x/evidence/module.go @@ -51,7 +51,7 @@ func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) { // DefaultGenesis returns the evidence module's default genesis state. func (AppModuleBasic) DefaultGenesis() json.RawMessage { // TODO: Return proper default genesis state. - return []byte("[]") + return []byte("{}") } // ValidateGenesis performs genesis state validation for the evidence module. From add9d0880734314009913ada00f89889fc211f56 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Mon, 28 Oct 2019 15:37:30 -0400 Subject: [PATCH 35/69] add message with test harness setup --- x/evidence/alias.go | 8 ++-- x/evidence/internal/keeper/keeper.go | 2 +- x/evidence/internal/types/errors.go | 4 +- x/evidence/internal/types/msgs.go | 58 ++++++++++++++++++++++++++ x/evidence/internal/types/msgs_test.go | 33 +++++++++++++++ x/evidence/internal/types/querier.go | 6 +-- 6 files changed, 102 insertions(+), 9 deletions(-) create mode 100644 x/evidence/internal/types/msgs.go create mode 100644 x/evidence/internal/types/msgs_test.go diff --git a/x/evidence/alias.go b/x/evidence/alias.go index d516a9bdf697..9ccd1b6f073c 100644 --- a/x/evidence/alias.go +++ b/x/evidence/alias.go @@ -18,6 +18,7 @@ const ( CodeNoEvidenceHandlerExists = types.CodeNoEvidenceHandlerExists CodeInvalidEvidence = types.CodeInvalidEvidence CodeNoEvidenceExists = types.CodeNoEvidenceExists + TypeMsgSubmitEvidence = types.TypeMsgSubmitEvidence ) var ( @@ -34,7 +35,8 @@ var ( type ( Keeper = keeper.Keeper - Evidence = types.Evidence - Handler = types.Handler - Router = types.Router + MsgSubmitEvidence = types.MsgSubmitEvidence + Evidence = types.Evidence + Handler = types.Handler + Router = types.Router ) diff --git a/x/evidence/internal/keeper/keeper.go b/x/evidence/internal/keeper/keeper.go index c1791c63648b..9ec48d966cea 100644 --- a/x/evidence/internal/keeper/keeper.go +++ b/x/evidence/internal/keeper/keeper.go @@ -70,7 +70,7 @@ func (k Keeper) SubmitEvidence(ctx sdk.Context, evidence types.Evidence) error { handler := k.router.GetRoute(evidence.Route()) if err := handler(ctx, evidence); err != nil { - return types.ErrInvalidEvidence(k.codespace, err) + return types.ErrInvalidEvidence(k.codespace, err.Error()) } k.setEvidence(ctx, evidence) diff --git a/x/evidence/internal/types/errors.go b/x/evidence/internal/types/errors.go index 72870685b340..38c88c8c2c1c 100644 --- a/x/evidence/internal/types/errors.go +++ b/x/evidence/internal/types/errors.go @@ -27,11 +27,11 @@ func ErrNoEvidenceHandlerExists(codespace sdk.CodespaceType, route string) error } // ErrInvalidEvidence returns a typed ABCI error for invalid evidence. -func ErrInvalidEvidence(codespace sdk.CodespaceType, err error) error { +func ErrInvalidEvidence(codespace sdk.CodespaceType, msg string) error { return sdkerrors.New( string(codespace), uint32(CodeInvalidEvidence), - fmt.Sprintf("invalid evidence: %s", err), + fmt.Sprintf("invalid evidence: %s", msg), ) } diff --git a/x/evidence/internal/types/msgs.go b/x/evidence/internal/types/msgs.go new file mode 100644 index 000000000000..12a8e5cddea3 --- /dev/null +++ b/x/evidence/internal/types/msgs.go @@ -0,0 +1,58 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +// Message types for the evidence module +const ( + TypeMsgSubmitEvidence = "submit_evidence" +) + +var ( + _ sdk.Msg = MsgSubmitEvidence{} +) + +// MsgSubmitEvidence defines an sdk.Msg type that supports submitting arbitrary +// Evidence. +type MsgSubmitEvidence struct { + Evidence Evidence `json:"evidence" yaml:"evidence"` + Submitter sdk.AccAddress `json:"submitter" yaml:"submitter"` +} + +func NewMsgSubmitEvidence(e Evidence, s sdk.AccAddress) MsgSubmitEvidence { + return MsgSubmitEvidence{Evidence: e, Submitter: s} +} + +// Route returns the MsgSubmitEvidence's route. +func (m MsgSubmitEvidence) Route() string { return RouterKey } + +// Type returns the MsgSubmitEvidence's type. +func (m MsgSubmitEvidence) Type() string { return TypeMsgSubmitEvidence } + +// ValidateBasic performs basic (non-state-dependant) validation on a MsgSubmitEvidence. +func (m MsgSubmitEvidence) ValidateBasic() sdk.Error { + if m.Evidence == nil { + return sdk.ConvertError(ErrInvalidEvidence(DefaultCodespace, "missing evidence")) + } + if err := m.Evidence.ValidateBasic(); err != nil { + return sdk.ConvertError(err) + } + if m.Submitter.Empty() { + return sdk.ConvertError(sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, m.Submitter.String())) + } + + return nil +} + +// GetSignBytes returns the raw bytes a signer is expected to sign when submitting +// a MsgSubmitEvidence message. +func (m MsgSubmitEvidence) GetSignBytes() []byte { + return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(m)) +} + +// GetSigners returns the single expected signer for a MsgSubmitEvidence. +func (m MsgSubmitEvidence) GetSigners() []sdk.AccAddress { + return []sdk.AccAddress{m.Submitter} +} diff --git a/x/evidence/internal/types/msgs_test.go b/x/evidence/internal/types/msgs_test.go new file mode 100644 index 000000000000..7e2e20bdf81e --- /dev/null +++ b/x/evidence/internal/types/msgs_test.go @@ -0,0 +1,33 @@ +package types_test + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/evidence/internal/types" + + "github.com/stretchr/testify/require" +) + +func TestMsgSubmitEvidence(t *testing.T) { + submitter := sdk.AccAddress("test") + testCases := []struct { + evidence types.Evidence + submitter sdk.AccAddress + expectErr bool + }{ + {nil, submitter, true}, + // TODO: Add test cases using real concrete types. + } + + for i, tc := range testCases { + msg := types.NewMsgSubmitEvidence(tc.evidence, tc.submitter) + require.Equal(t, msg.Route(), types.RouterKey, "unexpected result for tc #%d", i) + require.Equal(t, msg.Type(), types.TypeMsgSubmitEvidence, "unexpected result for tc #%d", i) + require.Equal(t, tc.expectErr, msg.ValidateBasic() != nil, "unexpected result for tc #%d", i) + + if !tc.expectErr { + require.Equal(t, msg.GetSigners(), []sdk.AccAddress{tc.submitter}, "unexpected result for tc #%d", i) + } + } +} diff --git a/x/evidence/internal/types/querier.go b/x/evidence/internal/types/querier.go index 1745884bb8b3..af643ee7c276 100644 --- a/x/evidence/internal/types/querier.go +++ b/x/evidence/internal/types/querier.go @@ -8,7 +8,7 @@ const ( // QueryEvidenceParams defines the parameters necessary for querying Evidence. type QueryEvidenceParams struct { - EvidenceHash string + EvidenceHash string `json:"evidence_hash" yaml:"evidence_hash"` } func NewQueryEvidenceParams(hash string) QueryEvidenceParams { @@ -17,8 +17,8 @@ func NewQueryEvidenceParams(hash string) QueryEvidenceParams { // QueryAllEvidenceParams defines the parameters necessary for querying for all Evidence. type QueryAllEvidenceParams struct { - Page int - Limit int + Page int `json:"page" yaml:"page"` + Limit int `json:"limit" yaml:"limit"` } func NewQueryAllEvidenceParams(page, limit int) QueryAllEvidenceParams { From fd3c6f2599b94687501edd1a13e2c6f1e7f8f54f Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Tue, 29 Oct 2019 10:32:47 -0400 Subject: [PATCH 36/69] register MsgSubmitEvidence with module cdc --- x/evidence/internal/types/codec.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/x/evidence/internal/types/codec.go b/x/evidence/internal/types/codec.go index 89a536ed6657..fd416db5e680 100644 --- a/x/evidence/internal/types/codec.go +++ b/x/evidence/internal/types/codec.go @@ -12,8 +12,7 @@ var ModuleCdc = codec.New() // evidence module. func RegisterCodec(cdc *codec.Codec) { cdc.RegisterInterface((*Evidence)(nil), nil) - - // TODO: Register concrete evidence types. + cdc.RegisterConcrete(MsgSubmitEvidence{}, "cosmos-sdk/MsgSubmitEvidence", nil) } // RegisterEvidenceTypeCodec registers an external concrete Evidence type defined From 129eb7ea46aa107ed36d645a7c63d89da868c4c4 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Tue, 29 Oct 2019 10:33:55 -0400 Subject: [PATCH 37/69] change export genesis type --- x/evidence/module.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/evidence/module.go b/x/evidence/module.go index 655de988337c..e7b1186398d1 100644 --- a/x/evidence/module.go +++ b/x/evidence/module.go @@ -146,7 +146,7 @@ func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.Va // ExportGenesis returns the evidence module's exported genesis state as raw JSON bytes. func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage { // TODO: Export genesis state and serialize. - return []byte("[]") + return []byte("{}") } // BeginBlock executes all ABCI BeginBlock logic respective to the evidence module. From 8aad1fb74ce8e6a7cab8914ac5e16b93fb780fb0 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Tue, 29 Oct 2019 11:18:10 -0400 Subject: [PATCH 38/69] redo type tests --- x/evidence/handler.go | 32 +++++++++++++++++ x/evidence/internal/keeper/keeper_test.go | 20 +++++------ x/evidence/internal/keeper/querier_test.go | 14 ++++---- x/evidence/internal/types/msgs_test.go | 34 ++++++++++++++++++- .../common_test.go => types/test_util.go} | 27 ++++++++------- x/evidence/module.go | 3 +- 6 files changed, 98 insertions(+), 32 deletions(-) create mode 100644 x/evidence/handler.go rename x/evidence/internal/{keeper/common_test.go => types/test_util.go} (82%) diff --git a/x/evidence/handler.go b/x/evidence/handler.go new file mode 100644 index 000000000000..b2882bae60c0 --- /dev/null +++ b/x/evidence/handler.go @@ -0,0 +1,32 @@ +package evidence + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func NewHandler(k Keeper) sdk.Handler { + return func(ctx sdk.Context, msg sdk.Msg) sdk.Result { + ctx = ctx.WithEventManager(sdk.NewEventManager()) + + switch msg := msg.(type) { + case MsgSubmitEvidence: + return handleMsgSubmitEvidence(ctx, k, msg) + + default: + return sdk.ErrUnknownRequest(fmt.Sprintf("unrecognized %s message type: %T", ModuleName, msg)).Result() + } + } +} + +func handleMsgSubmitEvidence(ctx sdk.Context, k Keeper, msg MsgSubmitEvidence) sdk.Result { + if err := k.SubmitEvidence(ctx, msg.Evidence); err != nil { + return sdk.ConvertError(err).Result() + } + + return sdk.Result{ + Data: msg.Evidence.Hash(), + Events: ctx.EventManager().Events(), + } +} diff --git a/x/evidence/internal/keeper/keeper_test.go b/x/evidence/internal/keeper/keeper_test.go index bd9c7b84a731..843fc9d278b1 100644 --- a/x/evidence/internal/keeper/keeper_test.go +++ b/x/evidence/internal/keeper/keeper_test.go @@ -33,13 +33,13 @@ func (suite *KeeperTestSuite) SetupTest() { storeKey := sdk.NewKVStoreKey(types.StoreKey) // create required keepers - paramsKeeper := params.NewKeeper(cdc, keyParams, tkeyParams, params.DefaultCodespace) + paramsKeeper := params.NewKeeper(types.TestingCdc, keyParams, tkeyParams, params.DefaultCodespace) subspace := paramsKeeper.Subspace(types.DefaultParamspace) - evidenceKeeper := keeper.NewKeeper(cdc, storeKey, subspace, types.DefaultCodespace) + evidenceKeeper := keeper.NewKeeper(types.TestingCdc, storeKey, subspace, types.DefaultCodespace) // create Evidence router, mount Handlers, and set keeper's router router := types.NewRouter() - router = router.AddRoute(EvidenceRouteEquivocation, EquivocationHandler(*evidenceKeeper)) + router = router.AddRoute(types.EvidenceRouteEquivocation, types.EquivocationHandler(*evidenceKeeper)) evidenceKeeper.SetRouter(router) // create DB, mount stores, and load latest version @@ -71,7 +71,7 @@ func (suite *KeeperTestSuite) populateEvidence(ctx sdk.Context, numEvidence int) for i := 0; i < numEvidence; i++ { pk := ed25519.GenPrivKey() - sv := SimpleVote{ + sv := types.SimpleVote{ ValidatorAddress: pk.PubKey().Address(), Height: int64(i), Round: 0, @@ -81,7 +81,7 @@ func (suite *KeeperTestSuite) populateEvidence(ctx sdk.Context, numEvidence int) suite.NoError(err) sv.Signature = sig - evidence[i] = EquivocationEvidence{ + evidence[i] = types.EquivocationEvidence{ Power: 100, TotalPower: 100000, PubKey: pk.PubKey(), @@ -98,7 +98,7 @@ func (suite *KeeperTestSuite) populateEvidence(ctx sdk.Context, numEvidence int) func (suite *KeeperTestSuite) TestSubmitValidEvidence() { ctx := suite.ctx.WithIsCheckTx(false) pk := ed25519.GenPrivKey() - sv := SimpleVote{ + sv := types.SimpleVote{ ValidatorAddress: pk.PubKey().Address(), Height: 11, Round: 0, @@ -108,7 +108,7 @@ func (suite *KeeperTestSuite) TestSubmitValidEvidence() { suite.NoError(err) sv.Signature = sig - e := EquivocationEvidence{ + e := types.EquivocationEvidence{ Power: 100, TotalPower: 100000, PubKey: pk.PubKey(), @@ -126,16 +126,16 @@ func (suite *KeeperTestSuite) TestSubmitValidEvidence() { func (suite *KeeperTestSuite) TestSubmitInvalidEvidence() { ctx := suite.ctx.WithIsCheckTx(false) pk := ed25519.GenPrivKey() - e := EquivocationEvidence{ + e := types.EquivocationEvidence{ Power: 100, TotalPower: 100000, PubKey: pk.PubKey(), - VoteA: SimpleVote{ + VoteA: types.SimpleVote{ ValidatorAddress: pk.PubKey().Address(), Height: 10, Round: 0, }, - VoteB: SimpleVote{ + VoteB: types.SimpleVote{ ValidatorAddress: pk.PubKey().Address(), Height: 11, Round: 0, diff --git a/x/evidence/internal/keeper/querier_test.go b/x/evidence/internal/keeper/querier_test.go index bce8f35291a4..e3dabb0c14f6 100644 --- a/x/evidence/internal/keeper/querier_test.go +++ b/x/evidence/internal/keeper/querier_test.go @@ -19,7 +19,7 @@ func (suite *KeeperTestSuite) TestQueryEvidence_Existing() { evidence := suite.populateEvidence(ctx, numEvidence) query := abci.RequestQuery{ Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryEvidence}, "/"), - Data: cdc.MustMarshalJSON(types.NewQueryEvidenceParams(evidence[0].Hash().String())), + Data: types.TestingCdc.MustMarshalJSON(types.NewQueryEvidenceParams(evidence[0].Hash().String())), } bz, err := suite.querier(ctx, []string{types.QueryEvidence}, query) @@ -27,7 +27,7 @@ func (suite *KeeperTestSuite) TestQueryEvidence_Existing() { suite.NotNil(bz) var e types.Evidence - suite.Nil(cdc.UnmarshalJSON(bz, &e)) + suite.Nil(types.TestingCdc.UnmarshalJSON(bz, &e)) suite.Equal(evidence[0], e) } @@ -38,7 +38,7 @@ func (suite *KeeperTestSuite) TestQueryEvidence_NonExisting() { suite.populateEvidence(ctx, numEvidence) query := abci.RequestQuery{ Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryEvidence}, "/"), - Data: cdc.MustMarshalJSON(types.NewQueryEvidenceParams("0000000000000000000000000000000000000000000000000000000000000000")), + Data: types.TestingCdc.MustMarshalJSON(types.NewQueryEvidenceParams("0000000000000000000000000000000000000000000000000000000000000000")), } bz, err := suite.querier(ctx, []string{types.QueryEvidence}, query) @@ -53,7 +53,7 @@ func (suite *KeeperTestSuite) TestQueryAllEvidence() { suite.populateEvidence(ctx, numEvidence) query := abci.RequestQuery{ Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryAllEvidence}, "/"), - Data: cdc.MustMarshalJSON(types.NewQueryAllEvidenceParams(1, numEvidence)), + Data: types.TestingCdc.MustMarshalJSON(types.NewQueryAllEvidenceParams(1, numEvidence)), } bz, err := suite.querier(ctx, []string{types.QueryAllEvidence}, query) @@ -61,7 +61,7 @@ func (suite *KeeperTestSuite) TestQueryAllEvidence() { suite.NotNil(bz) var e []types.Evidence - suite.Nil(cdc.UnmarshalJSON(bz, &e)) + suite.Nil(types.TestingCdc.UnmarshalJSON(bz, &e)) suite.Len(e, numEvidence) } @@ -72,7 +72,7 @@ func (suite *KeeperTestSuite) TestQueryAllEvidence_InvalidPagination() { suite.populateEvidence(ctx, numEvidence) query := abci.RequestQuery{ Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryAllEvidence}, "/"), - Data: cdc.MustMarshalJSON(types.NewQueryAllEvidenceParams(0, numEvidence)), + Data: types.TestingCdc.MustMarshalJSON(types.NewQueryAllEvidenceParams(0, numEvidence)), } bz, err := suite.querier(ctx, []string{types.QueryAllEvidence}, query) @@ -80,6 +80,6 @@ func (suite *KeeperTestSuite) TestQueryAllEvidence_InvalidPagination() { suite.NotNil(bz) var e []types.Evidence - suite.Nil(cdc.UnmarshalJSON(bz, &e)) + suite.Nil(types.TestingCdc.UnmarshalJSON(bz, &e)) suite.Len(e, 0) } diff --git a/x/evidence/internal/types/msgs_test.go b/x/evidence/internal/types/msgs_test.go index 7e2e20bdf81e..14df88985ac7 100644 --- a/x/evidence/internal/types/msgs_test.go +++ b/x/evidence/internal/types/msgs_test.go @@ -7,9 +7,20 @@ import ( "github.com/cosmos/cosmos-sdk/x/evidence/internal/types" "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/crypto/ed25519" ) func TestMsgSubmitEvidence(t *testing.T) { + pk := ed25519.GenPrivKey() + sv := types.SimpleVote{ + ValidatorAddress: pk.PubKey().Address(), + Height: 11, + Round: 0, + } + sig, err := pk.Sign(sv.SignBytes("test-chain")) + require.NoError(t, err) + sv.Signature = sig + submitter := sdk.AccAddress("test") testCases := []struct { evidence types.Evidence @@ -17,7 +28,28 @@ func TestMsgSubmitEvidence(t *testing.T) { expectErr bool }{ {nil, submitter, true}, - // TODO: Add test cases using real concrete types. + { + types.EquivocationEvidence{ + Power: 100, + TotalPower: 100000, + PubKey: pk.PubKey(), + VoteA: sv, + VoteB: sv, + }, + submitter, + false, + }, + { + types.EquivocationEvidence{ + Power: 100, + TotalPower: 100000, + PubKey: pk.PubKey(), + VoteA: sv, + VoteB: types.SimpleVote{Height: 10, Round: 1}, + }, + submitter, + true, + }, } for i, tc := range testCases { diff --git a/x/evidence/internal/keeper/common_test.go b/x/evidence/internal/types/test_util.go similarity index 82% rename from x/evidence/internal/keeper/common_test.go rename to x/evidence/internal/types/test_util.go index 5e19db726fcc..d31c8cd3fb1e 100644 --- a/x/evidence/internal/keeper/common_test.go +++ b/x/evidence/internal/types/test_util.go @@ -1,4 +1,8 @@ -package keeper_test +/* +Common testing types and utility functions and methods to be used in unit and +integration testing of the evidence module. +*/ +package types import ( "bytes" @@ -8,8 +12,6 @@ import ( "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/evidence/internal/keeper" - "github.com/cosmos/cosmos-sdk/x/evidence/internal/types" "gopkg.in/yaml.v2" @@ -19,8 +21,9 @@ import ( ) var ( - _ types.Evidence = (*EquivocationEvidence)(nil) - cdc = codec.New() + _ Evidence = (*EquivocationEvidence)(nil) + + TestingCdc = codec.New() ) const ( @@ -54,9 +57,9 @@ type ( ) func init() { - types.RegisterCodec(cdc) - codec.RegisterCrypto(cdc) - cdc.RegisterConcrete(EquivocationEvidence{}, "test/EquivocationEvidence", nil) + RegisterCodec(TestingCdc) + codec.RegisterCrypto(TestingCdc) + TestingCdc.RegisterConcrete(EquivocationEvidence{}, "test/EquivocationEvidence", nil) } func (e EquivocationEvidence) Route() string { return EvidenceRouteEquivocation } @@ -92,7 +95,7 @@ func (e EquivocationEvidence) ValidateBasic() error { } func (e EquivocationEvidence) Hash() cmn.HexBytes { - return tmhash.Sum(cdc.MustMarshalBinaryBare(e)) + return tmhash.Sum(TestingCdc.MustMarshalBinaryBare(e)) } func (v SimpleVote) SignBytes(chainID string) []byte { @@ -102,12 +105,12 @@ func (v SimpleVote) SignBytes(chainID string) []byte { Timestamp: v.Timestamp, ChainID: chainID, } - bz, _ := cdc.MarshalBinaryLengthPrefixed(scv) + bz, _ := TestingCdc.MarshalBinaryLengthPrefixed(scv) return bz } -func EquivocationHandler(k keeper.Keeper) types.Handler { - return func(ctx sdk.Context, e types.Evidence) error { +func EquivocationHandler(k interface{}) Handler { + return func(ctx sdk.Context, e Evidence) error { if err := e.ValidateBasic(); err != nil { return err } diff --git a/x/evidence/module.go b/x/evidence/module.go index e7b1186398d1..6da202694ea1 100644 --- a/x/evidence/module.go +++ b/x/evidence/module.go @@ -127,8 +127,7 @@ func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) { // NewHandler returns the evidence module's message Handler. func (am AppModule) NewHandler() sdk.Handler { - // TODO: Construct and return message Handler. - return nil + return NewHandler(am.keeper) } // NewQuerierHandler returns the evidence module's Querier. From 6374cd603fcedef5764b27e8686ddad64b13a6f1 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Tue, 29 Oct 2019 12:06:21 -0400 Subject: [PATCH 39/69] add handler unit tests --- x/evidence/alias.go | 3 + x/evidence/handler_test.go | 124 ++++++++++++++++++++++ x/evidence/internal/keeper/keeper_test.go | 16 +-- x/evidence/internal/types/msgs_test.go | 8 +- x/evidence/internal/types/test_util.go | 44 ++++---- 5 files changed, 161 insertions(+), 34 deletions(-) create mode 100644 x/evidence/handler_test.go diff --git a/x/evidence/alias.go b/x/evidence/alias.go index 9ccd1b6f073c..59f13ae73bf5 100644 --- a/x/evidence/alias.go +++ b/x/evidence/alias.go @@ -19,12 +19,15 @@ const ( CodeInvalidEvidence = types.CodeInvalidEvidence CodeNoEvidenceExists = types.CodeNoEvidenceExists TypeMsgSubmitEvidence = types.TypeMsgSubmitEvidence + DefaultCodespace = types.DefaultCodespace ) var ( NewKeeper = keeper.NewKeeper NewQuerier = keeper.NewQuerier + NewMsgSubmitEvidence = types.NewMsgSubmitEvidence + NewRouter = types.NewRouter NewQueryEvidenceParams = types.NewQueryEvidenceParams NewQueryAllEvidenceParams = types.NewQueryAllEvidenceParams RegisterCodec = types.RegisterCodec diff --git a/x/evidence/handler_test.go b/x/evidence/handler_test.go new file mode 100644 index 000000000000..4b3bde501c27 --- /dev/null +++ b/x/evidence/handler_test.go @@ -0,0 +1,124 @@ +package evidence_test + +import ( + "testing" + + "github.com/cosmos/cosmos-sdk/store" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/evidence" + "github.com/cosmos/cosmos-sdk/x/evidence/internal/types" + "github.com/cosmos/cosmos-sdk/x/params" + + "github.com/stretchr/testify/suite" + abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/crypto/ed25519" + "github.com/tendermint/tendermint/libs/log" + tmtypes "github.com/tendermint/tendermint/types" + dbm "github.com/tendermint/tm-db" +) + +type HandlerTestSuite struct { + suite.Suite + + ctx sdk.Context + handler sdk.Handler + keeper *evidence.Keeper +} + +func (suite *HandlerTestSuite) SetupTest() { + // create required store keys + keyParams := sdk.NewKVStoreKey(params.StoreKey) + tkeyParams := sdk.NewTransientStoreKey(params.TStoreKey) + storeKey := sdk.NewKVStoreKey(evidence.StoreKey) + + // create required keepers + paramsKeeper := params.NewKeeper(types.TestingCdc, keyParams, tkeyParams, params.DefaultCodespace) + subspace := paramsKeeper.Subspace(evidence.DefaultParamspace) + evidenceKeeper := evidence.NewKeeper(types.TestingCdc, storeKey, subspace, evidence.DefaultCodespace) + + // create Evidence router, mount Handlers, and set keeper's router + router := evidence.NewRouter() + router = router.AddRoute(types.TestEvidenceRouteEquivocation, types.TestEquivocationHandler(*evidenceKeeper)) + evidenceKeeper.SetRouter(router) + + // create DB, mount stores, and load latest version + db := dbm.NewMemDB() + cms := store.NewCommitMultiStore(db) + cms.MountStoreWithDB(keyParams, sdk.StoreTypeIAVL, db) + cms.MountStoreWithDB(tkeyParams, sdk.StoreTypeTransient, db) + cms.MountStoreWithDB(storeKey, sdk.StoreTypeIAVL, db) + suite.Nil(cms.LoadLatestVersion()) + + // create initial Context + ctx := sdk.NewContext(cms, abci.Header{ChainID: "test-chain"}, false, log.NewNopLogger()) + ctx = ctx.WithConsensusParams( + &abci.ConsensusParams{ + Validator: &abci.ValidatorParams{ + PubKeyTypes: []string{tmtypes.ABCIPubKeyTypeEd25519}, + }, + }, + ) + + suite.ctx = ctx + suite.handler = evidence.NewHandler(*evidenceKeeper) + suite.keeper = evidenceKeeper +} + +func (suite *HandlerTestSuite) TestMsgSubmitEvidence_Valid() { + pk := ed25519.GenPrivKey() + sv := types.TestVote{ + ValidatorAddress: pk.PubKey().Address(), + Height: 11, + Round: 0, + } + + sig, err := pk.Sign(sv.SignBytes("test-chain")) + suite.NoError(err) + sv.Signature = sig + + s := sdk.AccAddress("test") + e := types.TestEquivocationEvidence{ + Power: 100, + TotalPower: 100000, + PubKey: pk.PubKey(), + VoteA: sv, + VoteB: sv, + } + + ctx := suite.ctx.WithIsCheckTx(false) + msg := evidence.NewMsgSubmitEvidence(e, s) + res := suite.handler(ctx, msg) + suite.True(res.IsOK()) + suite.Equal(e.Hash().Bytes(), res.Data) +} + +func (suite *HandlerTestSuite) TestMsgSubmitEvidence_Invalid() { + pk := ed25519.GenPrivKey() + sv := types.TestVote{ + ValidatorAddress: pk.PubKey().Address(), + Height: 11, + Round: 0, + } + + sig, err := pk.Sign(sv.SignBytes("test-chain")) + suite.NoError(err) + sv.Signature = sig + + s := sdk.AccAddress("test") + e := types.TestEquivocationEvidence{ + Power: 100, + TotalPower: 100000, + PubKey: pk.PubKey(), + VoteA: sv, + VoteB: types.TestVote{Height: 10, Round: 1}, + } + + ctx := suite.ctx.WithIsCheckTx(false) + msg := evidence.NewMsgSubmitEvidence(e, s) + res := suite.handler(ctx, msg) + suite.False(res.IsOK()) +} + +func TestHandlerTestSuite(t *testing.T) { + suite.Run(t, new(HandlerTestSuite)) +} diff --git a/x/evidence/internal/keeper/keeper_test.go b/x/evidence/internal/keeper/keeper_test.go index 843fc9d278b1..8ab0489ddd02 100644 --- a/x/evidence/internal/keeper/keeper_test.go +++ b/x/evidence/internal/keeper/keeper_test.go @@ -39,7 +39,7 @@ func (suite *KeeperTestSuite) SetupTest() { // create Evidence router, mount Handlers, and set keeper's router router := types.NewRouter() - router = router.AddRoute(types.EvidenceRouteEquivocation, types.EquivocationHandler(*evidenceKeeper)) + router = router.AddRoute(types.TestEvidenceRouteEquivocation, types.TestEquivocationHandler(*evidenceKeeper)) evidenceKeeper.SetRouter(router) // create DB, mount stores, and load latest version @@ -71,7 +71,7 @@ func (suite *KeeperTestSuite) populateEvidence(ctx sdk.Context, numEvidence int) for i := 0; i < numEvidence; i++ { pk := ed25519.GenPrivKey() - sv := types.SimpleVote{ + sv := types.TestVote{ ValidatorAddress: pk.PubKey().Address(), Height: int64(i), Round: 0, @@ -81,7 +81,7 @@ func (suite *KeeperTestSuite) populateEvidence(ctx sdk.Context, numEvidence int) suite.NoError(err) sv.Signature = sig - evidence[i] = types.EquivocationEvidence{ + evidence[i] = types.TestEquivocationEvidence{ Power: 100, TotalPower: 100000, PubKey: pk.PubKey(), @@ -98,7 +98,7 @@ func (suite *KeeperTestSuite) populateEvidence(ctx sdk.Context, numEvidence int) func (suite *KeeperTestSuite) TestSubmitValidEvidence() { ctx := suite.ctx.WithIsCheckTx(false) pk := ed25519.GenPrivKey() - sv := types.SimpleVote{ + sv := types.TestVote{ ValidatorAddress: pk.PubKey().Address(), Height: 11, Round: 0, @@ -108,7 +108,7 @@ func (suite *KeeperTestSuite) TestSubmitValidEvidence() { suite.NoError(err) sv.Signature = sig - e := types.EquivocationEvidence{ + e := types.TestEquivocationEvidence{ Power: 100, TotalPower: 100000, PubKey: pk.PubKey(), @@ -126,16 +126,16 @@ func (suite *KeeperTestSuite) TestSubmitValidEvidence() { func (suite *KeeperTestSuite) TestSubmitInvalidEvidence() { ctx := suite.ctx.WithIsCheckTx(false) pk := ed25519.GenPrivKey() - e := types.EquivocationEvidence{ + e := types.TestEquivocationEvidence{ Power: 100, TotalPower: 100000, PubKey: pk.PubKey(), - VoteA: types.SimpleVote{ + VoteA: types.TestVote{ ValidatorAddress: pk.PubKey().Address(), Height: 10, Round: 0, }, - VoteB: types.SimpleVote{ + VoteB: types.TestVote{ ValidatorAddress: pk.PubKey().Address(), Height: 11, Round: 0, diff --git a/x/evidence/internal/types/msgs_test.go b/x/evidence/internal/types/msgs_test.go index 14df88985ac7..5d4e65943446 100644 --- a/x/evidence/internal/types/msgs_test.go +++ b/x/evidence/internal/types/msgs_test.go @@ -12,7 +12,7 @@ import ( func TestMsgSubmitEvidence(t *testing.T) { pk := ed25519.GenPrivKey() - sv := types.SimpleVote{ + sv := types.TestVote{ ValidatorAddress: pk.PubKey().Address(), Height: 11, Round: 0, @@ -29,7 +29,7 @@ func TestMsgSubmitEvidence(t *testing.T) { }{ {nil, submitter, true}, { - types.EquivocationEvidence{ + types.TestEquivocationEvidence{ Power: 100, TotalPower: 100000, PubKey: pk.PubKey(), @@ -40,12 +40,12 @@ func TestMsgSubmitEvidence(t *testing.T) { false, }, { - types.EquivocationEvidence{ + types.TestEquivocationEvidence{ Power: 100, TotalPower: 100000, PubKey: pk.PubKey(), VoteA: sv, - VoteB: types.SimpleVote{Height: 10, Round: 1}, + VoteB: types.TestVote{Height: 10, Round: 1}, }, submitter, true, diff --git a/x/evidence/internal/types/test_util.go b/x/evidence/internal/types/test_util.go index d31c8cd3fb1e..6d5788a48c4a 100644 --- a/x/evidence/internal/types/test_util.go +++ b/x/evidence/internal/types/test_util.go @@ -21,18 +21,18 @@ import ( ) var ( - _ Evidence = (*EquivocationEvidence)(nil) + _ Evidence = (*TestEquivocationEvidence)(nil) TestingCdc = codec.New() ) const ( - EvidenceRouteEquivocation = "EquivocationEvidence" - EvidenceTypeEquivocation = "equivocation" + TestEvidenceRouteEquivocation = "TestEquivocationEvidence" + TestEvidenceTypeEquivocation = "equivocation" ) type ( - SimpleVote struct { + TestVote struct { Height int64 Round int64 Timestamp time.Time @@ -40,44 +40,44 @@ type ( Signature []byte } - SimpleCanonicalVote struct { + TestCanonicalVote struct { Height int64 Round int64 Timestamp time.Time ChainID string } - EquivocationEvidence struct { + TestEquivocationEvidence struct { Power int64 TotalPower int64 PubKey crypto.PubKey - VoteA SimpleVote - VoteB SimpleVote + VoteA TestVote + VoteB TestVote } ) func init() { RegisterCodec(TestingCdc) codec.RegisterCrypto(TestingCdc) - TestingCdc.RegisterConcrete(EquivocationEvidence{}, "test/EquivocationEvidence", nil) + TestingCdc.RegisterConcrete(TestEquivocationEvidence{}, "test/TestEquivocationEvidence", nil) } -func (e EquivocationEvidence) Route() string { return EvidenceRouteEquivocation } -func (e EquivocationEvidence) Type() string { return EvidenceTypeEquivocation } -func (e EquivocationEvidence) GetHeight() int64 { return e.VoteA.Height } -func (e EquivocationEvidence) GetValidatorPower() int64 { return e.Power } -func (e EquivocationEvidence) GetTotalPower() int64 { return e.TotalPower } +func (e TestEquivocationEvidence) Route() string { return TestEvidenceRouteEquivocation } +func (e TestEquivocationEvidence) Type() string { return TestEvidenceTypeEquivocation } +func (e TestEquivocationEvidence) GetHeight() int64 { return e.VoteA.Height } +func (e TestEquivocationEvidence) GetValidatorPower() int64 { return e.Power } +func (e TestEquivocationEvidence) GetTotalPower() int64 { return e.TotalPower } -func (e EquivocationEvidence) String() string { +func (e TestEquivocationEvidence) String() string { bz, _ := yaml.Marshal(e) return string(bz) } -func (e EquivocationEvidence) GetConsensusAddress() sdk.ConsAddress { +func (e TestEquivocationEvidence) GetConsensusAddress() sdk.ConsAddress { return sdk.ConsAddress(e.PubKey.Address()) } -func (e EquivocationEvidence) ValidateBasic() error { +func (e TestEquivocationEvidence) ValidateBasic() error { if e.VoteA.Height != e.VoteB.Height || e.VoteA.Round != e.VoteB.Round { return fmt.Errorf("H/R/S does not match (got %v and %v)", e.VoteA, e.VoteB) @@ -94,12 +94,12 @@ func (e EquivocationEvidence) ValidateBasic() error { return nil } -func (e EquivocationEvidence) Hash() cmn.HexBytes { +func (e TestEquivocationEvidence) Hash() cmn.HexBytes { return tmhash.Sum(TestingCdc.MustMarshalBinaryBare(e)) } -func (v SimpleVote) SignBytes(chainID string) []byte { - scv := SimpleCanonicalVote{ +func (v TestVote) SignBytes(chainID string) []byte { + scv := TestCanonicalVote{ Height: v.Height, Round: v.Round, Timestamp: v.Timestamp, @@ -109,13 +109,13 @@ func (v SimpleVote) SignBytes(chainID string) []byte { return bz } -func EquivocationHandler(k interface{}) Handler { +func TestEquivocationHandler(k interface{}) Handler { return func(ctx sdk.Context, e Evidence) error { if err := e.ValidateBasic(); err != nil { return err } - ee, ok := e.(EquivocationEvidence) + ee, ok := e.(TestEquivocationEvidence) if !ok { return fmt.Errorf("unexpected evidence type: %T", e) } From 4ef7797d7bfa331442a4508a22d7084fe8b7b3cf Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Tue, 29 Oct 2019 12:08:40 -0400 Subject: [PATCH 40/69] add DONTCOVER to errors --- x/evidence/internal/types/errors.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/x/evidence/internal/types/errors.go b/x/evidence/internal/types/errors.go index 38c88c8c2c1c..15744b5c5e06 100644 --- a/x/evidence/internal/types/errors.go +++ b/x/evidence/internal/types/errors.go @@ -7,6 +7,8 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) +// DONTCOVER + // Error codes specific to the evidence module const ( DefaultCodespace sdk.CodespaceType = ModuleName From 25816ddb59d81eaa463b59a6a25cd4a3d0919e03 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Tue, 29 Oct 2019 14:07:50 -0400 Subject: [PATCH 41/69] add genesis tpes along with import/export --- x/evidence/alias.go | 6 ++-- x/evidence/genesis.go | 25 +++++++++++++++ x/evidence/internal/keeper/keeper.go | 8 +++-- x/evidence/internal/keeper/keeper_test.go | 29 +++++++++++++++++ x/evidence/internal/types/errors.go | 14 ++++++-- x/evidence/internal/types/genesis.go | 28 ++++++++++++++++ x/evidence/module.go | 39 ++++++++++++++--------- 7 files changed, 128 insertions(+), 21 deletions(-) create mode 100644 x/evidence/internal/types/genesis.go diff --git a/x/evidence/alias.go b/x/evidence/alias.go index 59f13ae73bf5..ae53d05c9da3 100644 --- a/x/evidence/alias.go +++ b/x/evidence/alias.go @@ -1,3 +1,4 @@ +// nolint package evidence import ( @@ -5,8 +6,6 @@ import ( "github.com/cosmos/cosmos-sdk/x/evidence/internal/types" ) -// nolint - const ( ModuleName = types.ModuleName StoreKey = types.StoreKey @@ -33,11 +32,14 @@ var ( RegisterCodec = types.RegisterCodec RegisterEvidenceTypeCodec = types.RegisterEvidenceTypeCodec ModuleCdc = types.ModuleCdc + NewGenesisState = types.NewGenesisState + DefaultGenesisState = types.DefaultGenesisState ) type ( Keeper = keeper.Keeper + GenesisState = types.GenesisState MsgSubmitEvidence = types.MsgSubmitEvidence Evidence = types.Evidence Handler = types.Handler diff --git a/x/evidence/genesis.go b/x/evidence/genesis.go index fc422ea0ab90..76305c0327b6 100644 --- a/x/evidence/genesis.go +++ b/x/evidence/genesis.go @@ -1 +1,26 @@ package evidence + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// InitGenesis initializes the evidence module's state from a provided genesis +// state. +func InitGenesis(ctx sdk.Context, k Keeper, gs GenesisState) { + for _, e := range gs.Evidence { + if _, ok := k.GetEvidence(ctx, e.Hash()); ok { + panic(fmt.Sprintf("evidence with hash %s already exists", e.Hash())) + } + + k.SetEvidence(ctx, e) + } +} + +// ExportGenesis returns the evidence module's exported genesis. +func ExportGenesis(ctx sdk.Context, k Keeper) GenesisState { + return GenesisState{ + Evidence: k.GetAllEvidence(ctx), + } +} diff --git a/x/evidence/internal/keeper/keeper.go b/x/evidence/internal/keeper/keeper.go index 9ec48d966cea..c2da04194931 100644 --- a/x/evidence/internal/keeper/keeper.go +++ b/x/evidence/internal/keeper/keeper.go @@ -64,6 +64,9 @@ func (k *Keeper) SetRouter(rtr types.Router) { // registered Handler exists or if the Handler fails. Otherwise, the evidence is // persisted. func (k Keeper) SubmitEvidence(ctx sdk.Context, evidence types.Evidence) error { + if _, ok := k.GetEvidence(ctx, evidence.Hash()); ok { + return types.ErrEvidenceExists(k.codespace, evidence.Hash().String()) + } if !k.router.HasRoute(evidence.Route()) { return types.ErrNoEvidenceHandlerExists(k.codespace, evidence.Route()) } @@ -73,11 +76,12 @@ func (k Keeper) SubmitEvidence(ctx sdk.Context, evidence types.Evidence) error { return types.ErrInvalidEvidence(k.codespace, err.Error()) } - k.setEvidence(ctx, evidence) + k.SetEvidence(ctx, evidence) return nil } -func (k Keeper) setEvidence(ctx sdk.Context, evidence types.Evidence) { +// SetEvidence sets Evidence by hash in the module's KVStore. +func (k Keeper) SetEvidence(ctx sdk.Context, evidence types.Evidence) { store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixEvidence) bz := k.cdc.MustMarshalBinaryLengthPrefixed(evidence) store.Set(evidence.Hash(), bz) diff --git a/x/evidence/internal/keeper/keeper_test.go b/x/evidence/internal/keeper/keeper_test.go index 8ab0489ddd02..cf80716782a9 100644 --- a/x/evidence/internal/keeper/keeper_test.go +++ b/x/evidence/internal/keeper/keeper_test.go @@ -123,6 +123,35 @@ func (suite *KeeperTestSuite) TestSubmitValidEvidence() { suite.Equal(e, res) } +func (suite *KeeperTestSuite) TestSubmitValidEvidence_Duplicate() { + ctx := suite.ctx.WithIsCheckTx(false) + pk := ed25519.GenPrivKey() + sv := types.TestVote{ + ValidatorAddress: pk.PubKey().Address(), + Height: 11, + Round: 0, + } + + sig, err := pk.Sign(sv.SignBytes(ctx.ChainID())) + suite.NoError(err) + sv.Signature = sig + + e := types.TestEquivocationEvidence{ + Power: 100, + TotalPower: 100000, + PubKey: pk.PubKey(), + VoteA: sv, + VoteB: sv, + } + + suite.Nil(suite.keeper.SubmitEvidence(ctx, e)) + suite.Error(suite.keeper.SubmitEvidence(ctx, e)) + + res, ok := suite.keeper.GetEvidence(ctx, e.Hash()) + suite.True(ok) + suite.Equal(e, res) +} + func (suite *KeeperTestSuite) TestSubmitInvalidEvidence() { ctx := suite.ctx.WithIsCheckTx(false) pk := ed25519.GenPrivKey() diff --git a/x/evidence/internal/types/errors.go b/x/evidence/internal/types/errors.go index 15744b5c5e06..2054cee9fb66 100644 --- a/x/evidence/internal/types/errors.go +++ b/x/evidence/internal/types/errors.go @@ -1,3 +1,4 @@ +// DONTCOVER package types import ( @@ -7,8 +8,6 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) -// DONTCOVER - // Error codes specific to the evidence module const ( DefaultCodespace sdk.CodespaceType = ModuleName @@ -16,6 +15,7 @@ const ( CodeNoEvidenceHandlerExists sdk.CodeType = 1 CodeInvalidEvidence sdk.CodeType = 2 CodeNoEvidenceExists sdk.CodeType = 3 + CodeEvidenceExists sdk.CodeType = 4 ) // ErrNoEvidenceHandlerExists returns a typed ABCI error for an invalid evidence @@ -46,3 +46,13 @@ func ErrNoEvidenceExists(codespace sdk.CodespaceType, hash string) error { fmt.Sprintf("evidence with hash %s does not exist", hash), ) } + +// ErrEvidenceExists returns a typed ABCI error for Evidence that already exists +// by hash in state. +func ErrEvidenceExists(codespace sdk.CodespaceType, hash string) error { + return sdkerrors.New( + string(codespace), + uint32(CodeEvidenceExists), + fmt.Sprintf("evidence with hash %s already exists", hash), + ) +} diff --git a/x/evidence/internal/types/genesis.go b/x/evidence/internal/types/genesis.go new file mode 100644 index 000000000000..9232873927d8 --- /dev/null +++ b/x/evidence/internal/types/genesis.go @@ -0,0 +1,28 @@ +// DONTCOVER +package types + +// GenesisState defines the evidence module's genesis state. +type GenesisState struct { + Evidence []Evidence `json:"evidence" yaml:"evidence"` +} + +func NewGenesisState() GenesisState { + return GenesisState{} +} + +// DefaultGenesisState returns the evidence module's default genesis state. +func DefaultGenesisState() GenesisState { + return GenesisState{} +} + +// Validate performs basic gensis state validation returning an error upon any +// failure. +func (gs GenesisState) Validate() error { + for _, e := range gs.Evidence { + if err := e.ValidateBasic(); err != nil { + return err + } + } + + return nil +} diff --git a/x/evidence/module.go b/x/evidence/module.go index 6da202694ea1..226bff404a7b 100644 --- a/x/evidence/module.go +++ b/x/evidence/module.go @@ -2,6 +2,7 @@ package evidence import ( "encoding/json" + "fmt" "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/codec" @@ -11,10 +12,9 @@ import ( "github.com/cosmos/cosmos-sdk/x/evidence/client/cli" "github.com/cosmos/cosmos-sdk/x/evidence/client/rest" - abci "github.com/tendermint/tendermint/abci/types" - "github.com/gorilla/mux" "github.com/spf13/cobra" + abci "github.com/tendermint/tendermint/abci/types" ) var ( @@ -50,14 +50,18 @@ func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) { // DefaultGenesis returns the evidence module's default genesis state. func (AppModuleBasic) DefaultGenesis() json.RawMessage { - // TODO: Return proper default genesis state. - return []byte("{}") + return ModuleCdc.MustMarshalJSON(DefaultGenesisState()) } // ValidateGenesis performs genesis state validation for the evidence module. func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { - // TODO: Validate genesis state. - return nil + var gs GenesisState + err := ModuleCdc.UnmarshalJSON(bz, &gs) + if err != nil { + return fmt.Errorf("failed to unmarshal %s genesis state: %w", ModuleName, err) + } + + return gs.Validate() } // RegisterRESTRoutes registers the evidence module's REST service handlers. @@ -120,11 +124,6 @@ func (AppModule) QuerierRoute() string { return QuerierRoute } -// RegisterInvariants registers the evidence module's invariants. -func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) { - // TODO: Register any necessary invariants (if any). -} - // NewHandler returns the evidence module's message Handler. func (am AppModule) NewHandler() sdk.Handler { return NewHandler(am.keeper) @@ -135,17 +134,27 @@ func (am AppModule) NewQuerierHandler() sdk.Querier { return NewQuerier(am.keeper) } +// RegisterInvariants registers the evidence module's invariants. +func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) { + // TODO: Register any necessary invariants (if any). +} + // InitGenesis performs the evidence module's genesis initialization It returns // no validator updates. -func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.ValidatorUpdate { - // TODO: Construct genesis state object and deserialize. +func (am AppModule) InitGenesis(ctx sdk.Context, bz json.RawMessage) []abci.ValidatorUpdate { + var gs GenesisState + err := ModuleCdc.UnmarshalJSON(bz, &gs) + if err != nil { + panic(fmt.Sprintf("failed to unmarshal %s genesis state: %s", ModuleName, err)) + } + + InitGenesis(ctx, am.keeper, gs) return []abci.ValidatorUpdate{} } // ExportGenesis returns the evidence module's exported genesis state as raw JSON bytes. func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage { - // TODO: Export genesis state and serialize. - return []byte("{}") + return ModuleCdc.MustMarshalJSON(ExportGenesis(ctx, am.keeper)) } // BeginBlock executes all ABCI BeginBlock logic respective to the evidence module. From 371ca0b7a142dc106f76c9fb45a357b606c665fb Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Tue, 29 Oct 2019 14:13:11 -0400 Subject: [PATCH 42/69] add genesis test file (stub) --- x/evidence/genesis_test.go | 1 + 1 file changed, 1 insertion(+) create mode 100644 x/evidence/genesis_test.go diff --git a/x/evidence/genesis_test.go b/x/evidence/genesis_test.go new file mode 100644 index 000000000000..56c3c318ca35 --- /dev/null +++ b/x/evidence/genesis_test.go @@ -0,0 +1 @@ +package evidence_test From 8c2413bbb8979a00256a9ab547547aeb1f7c9447 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 30 Oct 2019 13:27:51 -0400 Subject: [PATCH 43/69] use simapp in test suites --- simapp/app.go | 114 ++++++++++++++++------ x/evidence/handler_test.go | 53 +++------- x/evidence/internal/keeper/keeper_test.go | 58 ++++------- 3 files changed, 116 insertions(+), 109 deletions(-) diff --git a/simapp/app.go b/simapp/app.go index 53e671a1242d..e310e174906b 100644 --- a/simapp/app.go +++ b/simapp/app.go @@ -20,6 +20,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/bank" "github.com/cosmos/cosmos-sdk/x/crisis" distr "github.com/cosmos/cosmos-sdk/x/distribution" + "github.com/cosmos/cosmos-sdk/x/evidence" "github.com/cosmos/cosmos-sdk/x/genutil" "github.com/cosmos/cosmos-sdk/x/gov" "github.com/cosmos/cosmos-sdk/x/mint" @@ -54,6 +55,7 @@ var ( params.AppModuleBasic{}, crisis.AppModuleBasic{}, slashing.AppModuleBasic{}, + evidence.AppModuleBasic{}, ) // module account permissions @@ -90,6 +92,9 @@ type SimApp struct { keys map[string]*sdk.KVStoreKey tkeys map[string]*sdk.TransientStoreKey + // subspaces + subspaces map[string]params.Subspace + // keepers AccountKeeper auth.AccountKeeper BankKeeper bank.Keeper @@ -101,6 +106,7 @@ type SimApp struct { GovKeeper gov.Keeper CrisisKeeper crisis.Keeper ParamsKeeper params.Keeper + EvidenceKeeper evidence.Keeper // the module manager mm *module.Manager @@ -121,9 +127,10 @@ func NewSimApp( bApp.SetCommitMultiStoreTracer(traceStore) bApp.SetAppVersion(version.Version) - keys := sdk.NewKVStoreKeys(bam.MainStoreKey, auth.StoreKey, staking.StoreKey, - supply.StoreKey, mint.StoreKey, distr.StoreKey, slashing.StoreKey, - gov.StoreKey, params.StoreKey) + keys := sdk.NewKVStoreKeys( + bam.MainStoreKey, auth.StoreKey, staking.StoreKey, supply.StoreKey, mint.StoreKey, + distr.StoreKey, slashing.StoreKey, gov.StoreKey, params.StoreKey, evidence.StoreKey, + ) tkeys := sdk.NewTransientStoreKeys(params.TStoreKey) app := &SimApp{ @@ -132,39 +139,68 @@ func NewSimApp( invCheckPeriod: invCheckPeriod, keys: keys, tkeys: tkeys, + subspaces: make(map[string]params.Subspace), } // init params keeper and subspaces app.ParamsKeeper = params.NewKeeper(app.cdc, keys[params.StoreKey], tkeys[params.TStoreKey], params.DefaultCodespace) - authSubspace := app.ParamsKeeper.Subspace(auth.DefaultParamspace) - bankSubspace := app.ParamsKeeper.Subspace(bank.DefaultParamspace) - stakingSubspace := app.ParamsKeeper.Subspace(staking.DefaultParamspace) - mintSubspace := app.ParamsKeeper.Subspace(mint.DefaultParamspace) - distrSubspace := app.ParamsKeeper.Subspace(distr.DefaultParamspace) - slashingSubspace := app.ParamsKeeper.Subspace(slashing.DefaultParamspace) - govSubspace := app.ParamsKeeper.Subspace(gov.DefaultParamspace).WithKeyTable(gov.ParamKeyTable()) - crisisSubspace := app.ParamsKeeper.Subspace(crisis.DefaultParamspace) + app.subspaces[auth.ModuleName] = app.ParamsKeeper.Subspace(auth.DefaultParamspace) + app.subspaces[bank.ModuleName] = app.ParamsKeeper.Subspace(bank.DefaultParamspace) + app.subspaces[staking.ModuleName] = app.ParamsKeeper.Subspace(staking.DefaultParamspace) + app.subspaces[mint.ModuleName] = app.ParamsKeeper.Subspace(mint.DefaultParamspace) + app.subspaces[distr.ModuleName] = app.ParamsKeeper.Subspace(distr.DefaultParamspace) + app.subspaces[slashing.ModuleName] = app.ParamsKeeper.Subspace(slashing.DefaultParamspace) + app.subspaces[gov.ModuleName] = app.ParamsKeeper.Subspace(gov.DefaultParamspace).WithKeyTable(gov.ParamKeyTable()) + app.subspaces[crisis.ModuleName] = app.ParamsKeeper.Subspace(crisis.DefaultParamspace) + app.subspaces[evidence.ModuleName] = app.ParamsKeeper.Subspace(evidence.DefaultParamspace) // add keepers - app.AccountKeeper = auth.NewAccountKeeper(app.cdc, keys[auth.StoreKey], authSubspace, auth.ProtoBaseAccount) - app.BankKeeper = bank.NewBaseKeeper(app.AccountKeeper, bankSubspace, bank.DefaultCodespace, app.ModuleAccountAddrs()) - app.SupplyKeeper = supply.NewKeeper(app.cdc, keys[supply.StoreKey], app.AccountKeeper, app.BankKeeper, maccPerms) - stakingKeeper := staking.NewKeeper(app.cdc, keys[staking.StoreKey], - app.SupplyKeeper, stakingSubspace, staking.DefaultCodespace) - app.MintKeeper = mint.NewKeeper(app.cdc, keys[mint.StoreKey], mintSubspace, &stakingKeeper, app.SupplyKeeper, auth.FeeCollectorName) - app.DistrKeeper = distr.NewKeeper(app.cdc, keys[distr.StoreKey], distrSubspace, &stakingKeeper, - app.SupplyKeeper, distr.DefaultCodespace, auth.FeeCollectorName, app.ModuleAccountAddrs()) - app.SlashingKeeper = slashing.NewKeeper(app.cdc, keys[slashing.StoreKey], &stakingKeeper, - slashingSubspace, slashing.DefaultCodespace) - app.CrisisKeeper = crisis.NewKeeper(crisisSubspace, invCheckPeriod, app.SupplyKeeper, auth.FeeCollectorName) + app.AccountKeeper = auth.NewAccountKeeper( + app.cdc, keys[auth.StoreKey], app.subspaces[auth.ModuleName], auth.ProtoBaseAccount, + ) + app.BankKeeper = bank.NewBaseKeeper( + app.AccountKeeper, app.subspaces[bank.ModuleName], bank.DefaultCodespace, + app.ModuleAccountAddrs(), + ) + app.SupplyKeeper = supply.NewKeeper( + app.cdc, keys[supply.StoreKey], app.AccountKeeper, app.BankKeeper, maccPerms, + ) + stakingKeeper := staking.NewKeeper( + app.cdc, keys[staking.StoreKey], app.SupplyKeeper, app.subspaces[staking.ModuleName], + staking.DefaultCodespace) + app.MintKeeper = mint.NewKeeper( + app.cdc, keys[mint.StoreKey], app.subspaces[mint.ModuleName], &stakingKeeper, + app.SupplyKeeper, auth.FeeCollectorName, + ) + app.DistrKeeper = distr.NewKeeper( + app.cdc, keys[distr.StoreKey], app.subspaces[distr.ModuleName], &stakingKeeper, + app.SupplyKeeper, distr.DefaultCodespace, auth.FeeCollectorName, app.ModuleAccountAddrs(), + ) + app.SlashingKeeper = slashing.NewKeeper( + app.cdc, keys[slashing.StoreKey], &stakingKeeper, app.subspaces[slashing.ModuleName], slashing.DefaultCodespace, + ) + app.CrisisKeeper = crisis.NewKeeper( + app.subspaces[crisis.ModuleName], invCheckPeriod, app.SupplyKeeper, auth.FeeCollectorName, + ) + + // create evidence keeper with router + evidenceKeeper := evidence.NewKeeper( + app.cdc, keys[evidence.StoreKey], app.subspaces[evidence.ModuleName], evidence.DefaultCodespace, + ) + evidenceRouter := evidence.NewRouter() + // TODO: Register evidence routes. + evidenceKeeper.SetRouter(evidenceRouter) + app.EvidenceKeeper = *evidenceKeeper // register the proposal types govRouter := gov.NewRouter() govRouter.AddRoute(gov.RouterKey, gov.ProposalHandler). AddRoute(params.RouterKey, params.NewParamChangeProposalHandler(app.ParamsKeeper)). AddRoute(distr.RouterKey, distr.NewCommunityPoolSpendProposalHandler(app.DistrKeeper)) - app.GovKeeper = gov.NewKeeper(app.cdc, keys[gov.StoreKey], govSubspace, - app.SupplyKeeper, &stakingKeeper, gov.DefaultCodespace, govRouter) + app.GovKeeper = gov.NewKeeper( + app.cdc, keys[gov.StoreKey], app.subspaces[gov.ModuleName], app.SupplyKeeper, + &stakingKeeper, gov.DefaultCodespace, govRouter, + ) // register the staking hooks // NOTE: stakingKeeper above is passed by reference, so that it will contain these hooks @@ -185,22 +221,21 @@ func NewSimApp( distr.NewAppModule(app.DistrKeeper, app.SupplyKeeper), slashing.NewAppModule(app.SlashingKeeper, app.StakingKeeper), staking.NewAppModule(app.StakingKeeper, app.AccountKeeper, app.SupplyKeeper), + evidence.NewAppModule(app.EvidenceKeeper), ) // During begin block slashing happens after distr.BeginBlocker so that // there is nothing left over in the validator fee pool, so as to keep the // CanWithdrawInvariant invariant. app.mm.SetOrderBeginBlockers(mint.ModuleName, distr.ModuleName, slashing.ModuleName) - app.mm.SetOrderEndBlockers(crisis.ModuleName, gov.ModuleName, staking.ModuleName) // NOTE: The genutils moodule must occur after staking so that pools are // properly initialized with tokens from genesis accounts. app.mm.SetOrderInitGenesis( - auth.ModuleName, distr.ModuleName, staking.ModuleName, - bank.ModuleName, slashing.ModuleName, gov.ModuleName, - mint.ModuleName, supply.ModuleName, crisis.ModuleName, - genutil.ModuleName, + auth.ModuleName, distr.ModuleName, staking.ModuleName, bank.ModuleName, + slashing.ModuleName, gov.ModuleName, mint.ModuleName, supply.ModuleName, + crisis.ModuleName, genutil.ModuleName, evidence.ModuleName, ) app.mm.RegisterInvariants(&app.CrisisKeeper) @@ -239,6 +274,7 @@ func NewSimApp( cmn.Exit(err.Error()) } } + return app } @@ -274,21 +310,35 @@ func (app *SimApp) ModuleAccountAddrs() map[string]bool { return modAccAddrs } -// Codec returns simapp's codec +// Codec returns SimApp's codec. +// +// NOTE: This is solely to be used for testing purposes as it may be desirable +// for modules to register their own custom testing types. func (app *SimApp) Codec() *codec.Codec { return app.cdc } -// GetKey returns the KVStoreKey for the provided store key +// GetKey returns the KVStoreKey for the provided store key. +// +// NOTE: This is solely to be used for testing purposes. func (app *SimApp) GetKey(storeKey string) *sdk.KVStoreKey { return app.keys[storeKey] } -// GetTKey returns the TransientStoreKey for the provided store key +// GetTKey returns the TransientStoreKey for the provided store key. +// +// NOTE: This is solely to be used for testing purposes. func (app *SimApp) GetTKey(storeKey string) *sdk.TransientStoreKey { return app.tkeys[storeKey] } +// GetSubspace returns a param subspace for a given module name. +// +// NOTE: This is solely to be used for testing purposes. +func (app *SimApp) GetSubspace(moduleName string) params.Subspace { + return app.subspaces[moduleName] +} + // GetMaccPerms returns a copy of the module account permissions func GetMaccPerms() map[string][]string { dupMaccPerms := make(map[string][]string) diff --git a/x/evidence/handler_test.go b/x/evidence/handler_test.go index 4b3bde501c27..0b286817ee2c 100644 --- a/x/evidence/handler_test.go +++ b/x/evidence/handler_test.go @@ -3,18 +3,14 @@ package evidence_test import ( "testing" - "github.com/cosmos/cosmos-sdk/store" + "github.com/cosmos/cosmos-sdk/simapp" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/evidence" "github.com/cosmos/cosmos-sdk/x/evidence/internal/types" - "github.com/cosmos/cosmos-sdk/x/params" "github.com/stretchr/testify/suite" abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/crypto/ed25519" - "github.com/tendermint/tendermint/libs/log" - tmtypes "github.com/tendermint/tendermint/types" - dbm "github.com/tendermint/tm-db" ) type HandlerTestSuite struct { @@ -22,46 +18,29 @@ type HandlerTestSuite struct { ctx sdk.Context handler sdk.Handler - keeper *evidence.Keeper + keeper evidence.Keeper } func (suite *HandlerTestSuite) SetupTest() { - // create required store keys - keyParams := sdk.NewKVStoreKey(params.StoreKey) - tkeyParams := sdk.NewTransientStoreKey(params.TStoreKey) - storeKey := sdk.NewKVStoreKey(evidence.StoreKey) + checkTx := false + app := simapp.Setup(checkTx) - // create required keepers - paramsKeeper := params.NewKeeper(types.TestingCdc, keyParams, tkeyParams, params.DefaultCodespace) - subspace := paramsKeeper.Subspace(evidence.DefaultParamspace) - evidenceKeeper := evidence.NewKeeper(types.TestingCdc, storeKey, subspace, evidence.DefaultCodespace) + // get the app's codec and register custom testing types + cdc := app.Codec() + cdc.RegisterConcrete(types.TestEquivocationEvidence{}, "test/TestEquivocationEvidence", nil) - // create Evidence router, mount Handlers, and set keeper's router + // recreate keeper in order to use custom testing types + evidenceKeeper := evidence.NewKeeper( + cdc, app.GetKey(evidence.StoreKey), app.GetSubspace(evidence.ModuleName), + evidence.DefaultCodespace, + ) router := evidence.NewRouter() router = router.AddRoute(types.TestEvidenceRouteEquivocation, types.TestEquivocationHandler(*evidenceKeeper)) evidenceKeeper.SetRouter(router) - // create DB, mount stores, and load latest version - db := dbm.NewMemDB() - cms := store.NewCommitMultiStore(db) - cms.MountStoreWithDB(keyParams, sdk.StoreTypeIAVL, db) - cms.MountStoreWithDB(tkeyParams, sdk.StoreTypeTransient, db) - cms.MountStoreWithDB(storeKey, sdk.StoreTypeIAVL, db) - suite.Nil(cms.LoadLatestVersion()) - - // create initial Context - ctx := sdk.NewContext(cms, abci.Header{ChainID: "test-chain"}, false, log.NewNopLogger()) - ctx = ctx.WithConsensusParams( - &abci.ConsensusParams{ - Validator: &abci.ValidatorParams{ - PubKeyTypes: []string{tmtypes.ABCIPubKeyTypeEd25519}, - }, - }, - ) - - suite.ctx = ctx + suite.ctx = app.BaseApp.NewContext(checkTx, abci.Header{Height: 1}) suite.handler = evidence.NewHandler(*evidenceKeeper) - suite.keeper = evidenceKeeper + suite.keeper = *evidenceKeeper } func (suite *HandlerTestSuite) TestMsgSubmitEvidence_Valid() { @@ -72,7 +51,7 @@ func (suite *HandlerTestSuite) TestMsgSubmitEvidence_Valid() { Round: 0, } - sig, err := pk.Sign(sv.SignBytes("test-chain")) + sig, err := pk.Sign(sv.SignBytes(suite.ctx.ChainID())) suite.NoError(err) sv.Signature = sig @@ -100,7 +79,7 @@ func (suite *HandlerTestSuite) TestMsgSubmitEvidence_Invalid() { Round: 0, } - sig, err := pk.Sign(sv.SignBytes("test-chain")) + sig, err := pk.Sign(sv.SignBytes(suite.ctx.ChainID())) suite.NoError(err) sv.Signature = sig diff --git a/x/evidence/internal/keeper/keeper_test.go b/x/evidence/internal/keeper/keeper_test.go index cf80716782a9..310a8d3ef09e 100644 --- a/x/evidence/internal/keeper/keeper_test.go +++ b/x/evidence/internal/keeper/keeper_test.go @@ -3,67 +3,45 @@ package keeper_test import ( "testing" - "github.com/cosmos/cosmos-sdk/store" + "github.com/cosmos/cosmos-sdk/simapp" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/evidence" "github.com/cosmos/cosmos-sdk/x/evidence/internal/keeper" "github.com/cosmos/cosmos-sdk/x/evidence/internal/types" - "github.com/cosmos/cosmos-sdk/x/params" "github.com/stretchr/testify/suite" abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/crypto/ed25519" - "github.com/tendermint/tendermint/libs/log" - tmtypes "github.com/tendermint/tendermint/types" - dbm "github.com/tendermint/tm-db" ) type KeeperTestSuite struct { suite.Suite - cms sdk.CommitMultiStore ctx sdk.Context querier sdk.Querier - keeper *keeper.Keeper + keeper keeper.Keeper } func (suite *KeeperTestSuite) SetupTest() { - // create required store keys - keyParams := sdk.NewKVStoreKey(params.StoreKey) - tkeyParams := sdk.NewTransientStoreKey(params.TStoreKey) - storeKey := sdk.NewKVStoreKey(types.StoreKey) - - // create required keepers - paramsKeeper := params.NewKeeper(types.TestingCdc, keyParams, tkeyParams, params.DefaultCodespace) - subspace := paramsKeeper.Subspace(types.DefaultParamspace) - evidenceKeeper := keeper.NewKeeper(types.TestingCdc, storeKey, subspace, types.DefaultCodespace) - - // create Evidence router, mount Handlers, and set keeper's router - router := types.NewRouter() - router = router.AddRoute(types.TestEvidenceRouteEquivocation, types.TestEquivocationHandler(*evidenceKeeper)) - evidenceKeeper.SetRouter(router) + checkTx := false + app := simapp.Setup(checkTx) - // create DB, mount stores, and load latest version - db := dbm.NewMemDB() - cms := store.NewCommitMultiStore(db) - cms.MountStoreWithDB(keyParams, sdk.StoreTypeIAVL, db) - cms.MountStoreWithDB(tkeyParams, sdk.StoreTypeTransient, db) - cms.MountStoreWithDB(storeKey, sdk.StoreTypeIAVL, db) - suite.Nil(cms.LoadLatestVersion()) - - // create initial Context - ctx := sdk.NewContext(cms, abci.Header{ChainID: "test-chain"}, false, log.NewNopLogger()) - ctx = ctx.WithConsensusParams( - &abci.ConsensusParams{ - Validator: &abci.ValidatorParams{ - PubKeyTypes: []string{tmtypes.ABCIPubKeyTypeEd25519}, - }, - }, + // get the app's codec and register custom testing types + cdc := app.Codec() + cdc.RegisterConcrete(types.TestEquivocationEvidence{}, "test/TestEquivocationEvidence", nil) + + // recreate keeper in order to use custom testing types + evidenceKeeper := evidence.NewKeeper( + cdc, app.GetKey(evidence.StoreKey), app.GetSubspace(evidence.ModuleName), + evidence.DefaultCodespace, ) + router := evidence.NewRouter() + router = router.AddRoute(types.TestEvidenceRouteEquivocation, types.TestEquivocationHandler(*evidenceKeeper)) + evidenceKeeper.SetRouter(router) - suite.cms = cms - suite.ctx = ctx + suite.ctx = app.BaseApp.NewContext(checkTx, abci.Header{Height: 1}) suite.querier = keeper.NewQuerier(*evidenceKeeper) - suite.keeper = evidenceKeeper + suite.keeper = *evidenceKeeper } func (suite *KeeperTestSuite) populateEvidence(ctx sdk.Context, numEvidence int) []types.Evidence { From fd9a4847d181f93f27821f3f24ff2a19161655c5 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 30 Oct 2019 13:31:18 -0400 Subject: [PATCH 44/69] add DONTCOVER to test utils --- x/evidence/internal/types/test_util.go | 1 + 1 file changed, 1 insertion(+) diff --git a/x/evidence/internal/types/test_util.go b/x/evidence/internal/types/test_util.go index 6d5788a48c4a..98a3e706781a 100644 --- a/x/evidence/internal/types/test_util.go +++ b/x/evidence/internal/types/test_util.go @@ -2,6 +2,7 @@ Common testing types and utility functions and methods to be used in unit and integration testing of the evidence module. */ +// DONTCOVER package types import ( From 9b3976d483f7e0b24db9d7981fa6ea284d33d5c7 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 30 Oct 2019 13:59:25 -0400 Subject: [PATCH 45/69] implement genesis types unit tests --- x/evidence/internal/types/genesis.go | 6 +- x/evidence/internal/types/genesis_test.go | 69 +++++++++++++++++++++++ 2 files changed, 72 insertions(+), 3 deletions(-) create mode 100644 x/evidence/internal/types/genesis_test.go diff --git a/x/evidence/internal/types/genesis.go b/x/evidence/internal/types/genesis.go index 9232873927d8..521ea9004248 100644 --- a/x/evidence/internal/types/genesis.go +++ b/x/evidence/internal/types/genesis.go @@ -6,13 +6,13 @@ type GenesisState struct { Evidence []Evidence `json:"evidence" yaml:"evidence"` } -func NewGenesisState() GenesisState { - return GenesisState{} +func NewGenesisState(e []Evidence) GenesisState { + return GenesisState{Evidence: e} } // DefaultGenesisState returns the evidence module's default genesis state. func DefaultGenesisState() GenesisState { - return GenesisState{} + return GenesisState{Evidence: []Evidence{}} } // Validate performs basic gensis state validation returning an error upon any diff --git a/x/evidence/internal/types/genesis_test.go b/x/evidence/internal/types/genesis_test.go new file mode 100644 index 000000000000..2a3d93edde09 --- /dev/null +++ b/x/evidence/internal/types/genesis_test.go @@ -0,0 +1,69 @@ +package types_test + +import ( + "testing" + + "github.com/cosmos/cosmos-sdk/x/evidence/internal/types" + "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/crypto/ed25519" +) + +func TestDefaultGenesisState(t *testing.T) { + gs := types.DefaultGenesisState() + require.NotNil(t, gs.Evidence) + require.Len(t, gs.Evidence, 0) +} + +func TestGenesisStateValidate_Valid(t *testing.T) { + pk := ed25519.GenPrivKey() + + evidence := make([]types.Evidence, 100) + for i := 0; i < 100; i++ { + sv := types.TestVote{ + ValidatorAddress: pk.PubKey().Address(), + Height: int64(i), + Round: 0, + } + sig, err := pk.Sign(sv.SignBytes("test-chain")) + require.NoError(t, err) + sv.Signature = sig + + evidence[i] = types.TestEquivocationEvidence{ + Power: 100, + TotalPower: 100000, + PubKey: pk.PubKey(), + VoteA: sv, + VoteB: sv, + } + } + + gs := types.NewGenesisState(evidence) + require.NoError(t, gs.Validate()) +} + +func TestGenesisStateValidate_Invalid(t *testing.T) { + pk := ed25519.GenPrivKey() + + evidence := make([]types.Evidence, 100) + for i := 0; i < 100; i++ { + sv := types.TestVote{ + ValidatorAddress: pk.PubKey().Address(), + Height: int64(i), + Round: 0, + } + sig, err := pk.Sign(sv.SignBytes("test-chain")) + require.NoError(t, err) + sv.Signature = sig + + evidence[i] = types.TestEquivocationEvidence{ + Power: 100, + TotalPower: 100000, + PubKey: pk.PubKey(), + VoteA: sv, + VoteB: types.TestVote{Height: 10, Round: 1}, + } + } + + gs := types.NewGenesisState(evidence) + require.Error(t, gs.Validate()) +} From 8b21b3ba06a1f0e84907c078acad7396f8638f49 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 30 Oct 2019 15:22:00 -0400 Subject: [PATCH 46/69] add genesis tests --- x/evidence/genesis.go | 4 ++ x/evidence/genesis_test.go | 108 +++++++++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+) diff --git a/x/evidence/genesis.go b/x/evidence/genesis.go index 76305c0327b6..b06c7ce96783 100644 --- a/x/evidence/genesis.go +++ b/x/evidence/genesis.go @@ -9,6 +9,10 @@ import ( // InitGenesis initializes the evidence module's state from a provided genesis // state. func InitGenesis(ctx sdk.Context, k Keeper, gs GenesisState) { + if err := gs.Validate(); err != nil { + panic(fmt.Sprintf("failed to validate %s genesis state: %s", ModuleName, err)) + } + for _, e := range gs.Evidence { if _, ok := k.GetEvidence(ctx, e.Hash()); ok { panic(fmt.Sprintf("evidence with hash %s already exists", e.Hash())) diff --git a/x/evidence/genesis_test.go b/x/evidence/genesis_test.go index 56c3c318ca35..6a5e3fb2b87e 100644 --- a/x/evidence/genesis_test.go +++ b/x/evidence/genesis_test.go @@ -1 +1,109 @@ package evidence_test + +import ( + "testing" + + "github.com/cosmos/cosmos-sdk/simapp" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/evidence" + "github.com/cosmos/cosmos-sdk/x/evidence/internal/types" + abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/crypto/ed25519" + + "github.com/stretchr/testify/suite" +) + +type GenesisTestSuite struct { + suite.Suite + + ctx sdk.Context + keeper evidence.Keeper +} + +func (suite *GenesisTestSuite) SetupTest() { + checkTx := false + app := simapp.Setup(checkTx) + + // get the app's codec and register custom testing types + cdc := app.Codec() + cdc.RegisterConcrete(types.TestEquivocationEvidence{}, "test/TestEquivocationEvidence", nil) + + // recreate keeper in order to use custom testing types + evidenceKeeper := evidence.NewKeeper( + cdc, app.GetKey(evidence.StoreKey), app.GetSubspace(evidence.ModuleName), + evidence.DefaultCodespace, + ) + router := evidence.NewRouter() + router = router.AddRoute(types.TestEvidenceRouteEquivocation, types.TestEquivocationHandler(*evidenceKeeper)) + evidenceKeeper.SetRouter(router) + + suite.ctx = app.BaseApp.NewContext(checkTx, abci.Header{Height: 1}) + suite.keeper = *evidenceKeeper +} + +func (suite *GenesisTestSuite) TestInitGenesis_Valid() { + pk := ed25519.GenPrivKey() + + testEvidence := make([]types.Evidence, 100) + for i := 0; i < 100; i++ { + sv := types.TestVote{ + ValidatorAddress: pk.PubKey().Address(), + Height: int64(i), + Round: 0, + } + sig, err := pk.Sign(sv.SignBytes("test-chain")) + suite.NoError(err) + sv.Signature = sig + + testEvidence[i] = types.TestEquivocationEvidence{ + Power: 100, + TotalPower: 100000, + PubKey: pk.PubKey(), + VoteA: sv, + VoteB: sv, + } + } + + suite.NotPanics(func() { + evidence.InitGenesis(suite.ctx, suite.keeper, evidence.NewGenesisState(testEvidence)) + }) + + for _, e := range testEvidence { + _, ok := suite.keeper.GetEvidence(suite.ctx, e.Hash()) + suite.True(ok) + } +} + +func (suite *GenesisTestSuite) TestInitGenesis_Invalid() { + pk := ed25519.GenPrivKey() + + testEvidence := make([]types.Evidence, 100) + for i := 0; i < 100; i++ { + sv := types.TestVote{ + ValidatorAddress: pk.PubKey().Address(), + Height: int64(i), + Round: 0, + } + sig, err := pk.Sign(sv.SignBytes("test-chain")) + suite.NoError(err) + sv.Signature = sig + + testEvidence[i] = types.TestEquivocationEvidence{ + Power: 100, + TotalPower: 100000, + PubKey: pk.PubKey(), + VoteA: sv, + VoteB: types.TestVote{Height: 10, Round: 1}, + } + } + + suite.Panics(func() { + evidence.InitGenesis(suite.ctx, suite.keeper, evidence.NewGenesisState(testEvidence)) + }) + + suite.Empty(suite.keeper.GetAllEvidence(suite.ctx)) +} + +func TestGenesisTestSuite(t *testing.T) { + suite.Run(t, new(GenesisTestSuite)) +} From 8e8d382ea58133f02ed449ae6dbec2a0310186e4 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 30 Oct 2019 15:46:50 -0400 Subject: [PATCH 47/69] add doc.go --- x/evidence/alias.go | 3 ++- x/evidence/doc.go | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/x/evidence/alias.go b/x/evidence/alias.go index ae53d05c9da3..8f2d2bdb8737 100644 --- a/x/evidence/alias.go +++ b/x/evidence/alias.go @@ -1,4 +1,3 @@ -// nolint package evidence import ( @@ -6,6 +5,8 @@ import ( "github.com/cosmos/cosmos-sdk/x/evidence/internal/types" ) +// nolint + const ( ModuleName = types.ModuleName StoreKey = types.StoreKey diff --git a/x/evidence/doc.go b/x/evidence/doc.go index fc422ea0ab90..f250188f4783 100644 --- a/x/evidence/doc.go +++ b/x/evidence/doc.go @@ -1 +1,44 @@ +/* +Package evidence implements a Cosmos SDK module, per ADR 009, that allows for the +submission and handling of arbitrary evidence of misbehavior. + +All concrete evidence types must implement the Evidence interface contract. Sumbitted +evidence is first routed through the evidence module's Router in which it attempts +to find a corresponding Handler for that specific evidence type. Each evidence type +must have a Handler registered with the evidence module's keeper in order for it +to be successfully executed. + +Each corresponding handler must also fulfill the Handler interface contract. The +Handler for a given Evidence type can perform any arbitrary state transitions +such as slashing, jailing, and tombstoning. This provides developers with great +flexibility in designing evidence handling. + +A full setup of the evidence module may look something as follows: + + ModuleBasics = module.NewBasicManager( + // ..., + evidence.AppModuleBasic{}, + ) + + // First, create the keeper's subspace for parameters and the keeper itself. + evidenceParamspace := app.ParamsKeeper.Subspace(evidence.DefaultParamspace) + evidenceKeeper := evidence.NewKeeper( + app.cdc, keys[evidence.StoreKey], evidenceParamspace, evidence.DefaultCodespace, + ) + + + // Second, create the evidence Handler and register all desired routes. + evidenceRouter := evidence.NewRouter(). + AddRoute(evidenceRoute, evidenceHandler). + AddRoute(..., ...) + + evidenceKeeper.SetRouter(evidenceRouter) + + app.mm = module.NewManager( + // ... + evidence.NewAppModule(evidenceKeeper), + ) + + // Remaining application bootstrapping... +*/ package evidence From 2e1485ff13937953753ba24904a8c5f06c3e2422 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 30 Oct 2019 15:49:40 -0400 Subject: [PATCH 48/69] remove TODO from module --- x/evidence/module.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/x/evidence/module.go b/x/evidence/module.go index 226bff404a7b..50ce4f850433 100644 --- a/x/evidence/module.go +++ b/x/evidence/module.go @@ -158,13 +158,10 @@ func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage { } // BeginBlock executes all ABCI BeginBlock logic respective to the evidence module. -func (AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) { - // TODO: Execute BeginBlocker (if applicable). -} +func (AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) {} // EndBlock executes all ABCI EndBlock logic respective to the evidence module. It // returns no validator updates. func (am AppModule) EndBlock(ctx sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { - // TODO: Execute EndBlocker (if applicable). return []abci.ValidatorUpdate{} } From f3c9d8b32108065c917b2ac5529aa1d4818da1f8 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 30 Oct 2019 16:25:24 -0400 Subject: [PATCH 49/69] implement CLI query command --- x/evidence/client/cli/query.go | 102 +++++++++++++++++++++++++++++++-- 1 file changed, 97 insertions(+), 5 deletions(-) diff --git a/x/evidence/client/cli/query.go b/x/evidence/client/cli/query.go index 6949f5a329ef..53963d324614 100644 --- a/x/evidence/client/cli/query.go +++ b/x/evidence/client/cli/query.go @@ -1,25 +1,117 @@ package cli import ( + "encoding/hex" + "fmt" + "strings" + "github.com/spf13/cobra" + "github.com/spf13/viper" "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/x/evidence/internal/types" + "github.com/cosmos/cosmos-sdk/version" + "github.com/cosmos/cosmos-sdk/x/evidence" +) + +const ( + flagPage = "page" + flagLimit = "limit" ) // GetQueryCmd returns the CLI command with all evidence module query commands // mounted. func GetQueryCmd(queryRoute string, cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ - Use: types.ModuleName, - Short: "Evidence query subcommands", + Use: evidence.ModuleName, + Short: "Query for evidence by hash or for all (paginated) submitted evidence", + Long: strings.TrimSpace( + fmt.Sprintf(`Query for specific submitted evidence by hash or query for all (paginated) evidence: + +Example: +$ %s query %s DF0C23E8634E480F84B9D5674A7CDC9816466DEC28A3358F73260F68D28D7660 +$ %s query %s --page=2 --limit=50 +`, + version.ClientName, evidence.ModuleName, version.ClientName, evidence.ModuleName, + ), + ), + Args: cobra.MaximumNArgs(1), DisableFlagParsing: true, SuggestionsMinimumDistance: 2, - RunE: client.ValidateCmd, + RunE: QueryEvidenceCMD(cdc), } - // TODO: Add query commands. + cmd.Flags().Int(flagPage, 1, "pagination page of evidence to to query for") + cmd.Flags().Int(flagLimit, 100, "pagination limit of evidence to query for") return cmd } + +// QueryEvidenceCMD returns the command handler for evidence querying. Evidence +// can be queried for by hash or paginated evidence can be returned. +func QueryEvidenceCMD(cdc *codec.Codec) func(*cobra.Command, []string) error { + return func(cmd *cobra.Command, args []string) error { + if err := client.ValidateCmd(cmd, args); err != nil { + return err + } + + cliCtx := context.NewCLIContext().WithCodec(cdc) + + if hash := args[0]; hash != "" { + return queryEvidence(cdc, cliCtx, hash) + } + + return queryAllEvidence(cdc, cliCtx) + } +} + +func queryEvidence(cdc *codec.Codec, cliCtx context.CLIContext, hash string) error { + if _, err := hex.DecodeString(hash); err != nil { + return fmt.Errorf("invalid evidence hash: %w", err) + } + + params := evidence.NewQueryEvidenceParams(hash) + + bz, err := cdc.MarshalJSON(params) + if err != nil { + return fmt.Errorf("failed to marshal query params: %w", err) + } + + route := fmt.Sprintf("custom/%s/%s", evidence.QuerierRoute, evidence.QueryEvidence) + res, _, err := cliCtx.QueryWithData(route, bz) + if err != nil { + return err + } + + var evidence evidence.Evidence + err = cdc.UnmarshalJSON(res, &evidence) + if err != nil { + return fmt.Errorf("failed to unmarshal evidence: %w", err) + } + + return cliCtx.PrintOutput(evidence) +} + +func queryAllEvidence(cdc *codec.Codec, cliCtx context.CLIContext) error { + params := evidence.NewQueryAllEvidenceParams(viper.GetInt(flagPage), viper.GetInt(flagLimit)) + + bz, err := cdc.MarshalJSON(params) + if err != nil { + return fmt.Errorf("failed to marshal query params: %w", err) + } + + route := fmt.Sprintf("custom/%s/%s", evidence.QuerierRoute, evidence.QueryAllEvidence) + res, _, err := cliCtx.QueryWithData(route, bz) + if err != nil { + return err + } + + var evidence []evidence.Evidence + err = cdc.UnmarshalJSON(res, &evidence) + if err != nil { + return fmt.Errorf("failed to unmarshal evidence: %w", err) + } + + return cliCtx.PrintOutput(evidence) +} From 5a727fd048e14ecb384adbe1752f226bbb6890f3 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 30 Oct 2019 16:26:21 -0400 Subject: [PATCH 50/69] remove TODO from module --- x/evidence/module.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/x/evidence/module.go b/x/evidence/module.go index 50ce4f850433..d373a6819eee 100644 --- a/x/evidence/module.go +++ b/x/evidence/module.go @@ -135,9 +135,7 @@ func (am AppModule) NewQuerierHandler() sdk.Querier { } // RegisterInvariants registers the evidence module's invariants. -func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) { - // TODO: Register any necessary invariants (if any). -} +func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) {} // InitGenesis performs the evidence module's genesis initialization It returns // no validator updates. From ba048c624b84fca3260da7fef5c7ac51b2c00078 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Thu, 31 Oct 2019 11:56:20 -0400 Subject: [PATCH 51/69] implement rest handlers to query for evidence --- x/evidence/client/cli/query.go | 20 ++++---- x/evidence/client/rest/query.go | 81 ++++++++++++++++++++++++++++++++- x/evidence/client/rest/rest.go | 7 +++ x/evidence/module.go | 2 + 4 files changed, 98 insertions(+), 12 deletions(-) diff --git a/x/evidence/client/cli/query.go b/x/evidence/client/cli/query.go index 53963d324614..207c25dcf538 100644 --- a/x/evidence/client/cli/query.go +++ b/x/evidence/client/cli/query.go @@ -12,7 +12,7 @@ import ( "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/version" - "github.com/cosmos/cosmos-sdk/x/evidence" + "github.com/cosmos/cosmos-sdk/x/evidence/internal/types" ) const ( @@ -24,7 +24,7 @@ const ( // mounted. func GetQueryCmd(queryRoute string, cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ - Use: evidence.ModuleName, + Use: types.ModuleName, Short: "Query for evidence by hash or for all (paginated) submitted evidence", Long: strings.TrimSpace( fmt.Sprintf(`Query for specific submitted evidence by hash or query for all (paginated) evidence: @@ -33,7 +33,7 @@ Example: $ %s query %s DF0C23E8634E480F84B9D5674A7CDC9816466DEC28A3358F73260F68D28D7660 $ %s query %s --page=2 --limit=50 `, - version.ClientName, evidence.ModuleName, version.ClientName, evidence.ModuleName, + version.ClientName, types.ModuleName, version.ClientName, types.ModuleName, ), ), Args: cobra.MaximumNArgs(1), @@ -71,20 +71,19 @@ func queryEvidence(cdc *codec.Codec, cliCtx context.CLIContext, hash string) err return fmt.Errorf("invalid evidence hash: %w", err) } - params := evidence.NewQueryEvidenceParams(hash) - + params := types.NewQueryEvidenceParams(hash) bz, err := cdc.MarshalJSON(params) if err != nil { return fmt.Errorf("failed to marshal query params: %w", err) } - route := fmt.Sprintf("custom/%s/%s", evidence.QuerierRoute, evidence.QueryEvidence) + route := fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryEvidence) res, _, err := cliCtx.QueryWithData(route, bz) if err != nil { return err } - var evidence evidence.Evidence + var evidence types.Evidence err = cdc.UnmarshalJSON(res, &evidence) if err != nil { return fmt.Errorf("failed to unmarshal evidence: %w", err) @@ -94,20 +93,19 @@ func queryEvidence(cdc *codec.Codec, cliCtx context.CLIContext, hash string) err } func queryAllEvidence(cdc *codec.Codec, cliCtx context.CLIContext) error { - params := evidence.NewQueryAllEvidenceParams(viper.GetInt(flagPage), viper.GetInt(flagLimit)) - + params := types.NewQueryAllEvidenceParams(viper.GetInt(flagPage), viper.GetInt(flagLimit)) bz, err := cdc.MarshalJSON(params) if err != nil { return fmt.Errorf("failed to marshal query params: %w", err) } - route := fmt.Sprintf("custom/%s/%s", evidence.QuerierRoute, evidence.QueryAllEvidence) + route := fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryAllEvidence) res, _, err := cliCtx.QueryWithData(route, bz) if err != nil { return err } - var evidence []evidence.Evidence + var evidence []types.Evidence err = cdc.UnmarshalJSON(res, &evidence) if err != nil { return fmt.Errorf("failed to unmarshal evidence: %w", err) diff --git a/x/evidence/client/rest/query.go b/x/evidence/client/rest/query.go index 73b5d4a42282..050808f112dd 100644 --- a/x/evidence/client/rest/query.go +++ b/x/evidence/client/rest/query.go @@ -1,11 +1,90 @@ package rest import ( + "fmt" + "net/http" + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/types/rest" + "github.com/cosmos/cosmos-sdk/x/evidence/internal/types" "github.com/gorilla/mux" ) func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router) { - // TODO: Register query handlers. + r.HandleFunc( + fmt.Sprintf("/evidence/{%s}", RestParamEvidenceHash), + queryEvidenceHandler(cliCtx), + ).Methods(MethodGet) + + r.HandleFunc( + "/evidence", + queryAllEvidenceHandler(cliCtx), + ).Methods(MethodGet) +} + +func queryEvidenceHandler(cliCtx context.CLIContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + evidenceHash := vars[RestParamEvidenceHash] + + if len(evidenceHash) == 0 { + rest.WriteErrorResponse(w, http.StatusBadRequest, "evidence hash required but not specified") + return + } + + cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + if !ok { + return + } + + params := types.NewQueryEvidenceParams(evidenceHash) + bz, err := cliCtx.Codec.MarshalJSON(params) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, fmt.Sprintf("failed to marshal query params: %s", err)) + return + } + + route := fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryEvidence) + res, height, err := cliCtx.QueryWithData(route, bz) + if err != nil { + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + + cliCtx = cliCtx.WithHeight(height) + rest.PostProcessResponse(w, cliCtx, res) + } +} + +func queryAllEvidenceHandler(cliCtx context.CLIContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + _, page, limit, err := rest.ParseHTTPArgsWithLimit(r, 0) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r) + if !ok { + return + } + + params := types.NewQueryAllEvidenceParams(page, limit) + bz, err := cliCtx.Codec.MarshalJSON(params) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, fmt.Sprintf("failed to marshal query params: %s", err)) + return + } + + route := fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryAllEvidence) + res, height, err := cliCtx.QueryWithData(route, bz) + if err != nil { + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + + cliCtx = cliCtx.WithHeight(height) + rest.PostProcessResponse(w, cliCtx, res) + } } diff --git a/x/evidence/client/rest/rest.go b/x/evidence/client/rest/rest.go index 737f0a3ddc35..bb66afbbc073 100644 --- a/x/evidence/client/rest/rest.go +++ b/x/evidence/client/rest/rest.go @@ -8,6 +8,13 @@ import ( "github.com/gorilla/mux" ) +// REST query and parameter values +const ( + RestParamEvidenceHash = "evidence-hash" + + MethodGet = "GET" +) + // EvidenceRESTHandler defines a REST service evidence handler implemented in // another module. The sub-route is mounted on the evidence REST handler. type EvidenceRESTHandler struct { diff --git a/x/evidence/module.go b/x/evidence/module.go index d373a6819eee..a66a35adf36c 100644 --- a/x/evidence/module.go +++ b/x/evidence/module.go @@ -20,6 +20,8 @@ import ( var ( _ module.AppModule = AppModule{} _ module.AppModuleBasic = AppModuleBasic{} + + // TODO: Enable simulation once concrete types are defined. // _ module.AppModuleSimulation = AppModuleSimulation{} ) From 2dfe2e59dc70b05b1801b0acb83fb9d24ea084b2 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Thu, 31 Oct 2019 12:06:50 -0400 Subject: [PATCH 52/69] update SubmitEvidenceCMD --- x/evidence/client/cli/tx.go | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/x/evidence/client/cli/tx.go b/x/evidence/client/cli/tx.go index da03bad8d7e0..cd7aac6177d8 100644 --- a/x/evidence/client/cli/tx.go +++ b/x/evidence/client/cli/tx.go @@ -22,7 +22,7 @@ func GetTxCmd(storeKey string, cdc *codec.Codec, childCmds []*cobra.Command) *co RunE: client.ValidateCmd, } - submitEvidenceCmd := getSubmitEvidenceCmd(cdc) + submitEvidenceCmd := SubmitEvidenceCMD(cdc) for _, childCmd := range childCmds { submitEvidenceCmd.AddCommand(client.PostCommands(childCmd)[0]) } @@ -32,7 +32,14 @@ func GetTxCmd(storeKey string, cdc *codec.Codec, childCmds []*cobra.Command) *co return cmd } -func getSubmitEvidenceCmd(cdc *codec.Codec) *cobra.Command { - // TODO: Implement and return 'submit-evidence sub-command' - return nil +// SubmitEvidenceCMD returns the top-level evidence submission command handler. +// All concrete evidence submission child command handlers should be registered +// under this command. +func SubmitEvidenceCMD(cdc *codec.Codec) *cobra.Command { + cmd := &cobra.Command{ + Use: "submit", + Short: "Submit arbitrary evidence of misbehavior", + } + + return cmd } From 615a3c06073039007e65f6c89d821d4519656516 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Thu, 31 Oct 2019 13:39:53 -0400 Subject: [PATCH 53/69] add spec --- x/evidence/doc.go | 2 +- x/evidence/spec/01_concepts.md | 58 ++++++++++++++++++++++++++++++++++ x/evidence/spec/02_state.md | 12 +++++++ x/evidence/spec/03_messages.md | 42 ++++++++++++++++++++++++ x/evidence/spec/README.md | 27 ++++++++++++++++ 5 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 x/evidence/spec/01_concepts.md create mode 100644 x/evidence/spec/02_state.md create mode 100644 x/evidence/spec/03_messages.md create mode 100644 x/evidence/spec/README.md diff --git a/x/evidence/doc.go b/x/evidence/doc.go index f250188f4783..23fd4c9aed01 100644 --- a/x/evidence/doc.go +++ b/x/evidence/doc.go @@ -2,7 +2,7 @@ Package evidence implements a Cosmos SDK module, per ADR 009, that allows for the submission and handling of arbitrary evidence of misbehavior. -All concrete evidence types must implement the Evidence interface contract. Sumbitted +All concrete evidence types must implement the Evidence interface contract. Submitted evidence is first routed through the evidence module's Router in which it attempts to find a corresponding Handler for that specific evidence type. Each evidence type must have a Handler registered with the evidence module's keeper in order for it diff --git a/x/evidence/spec/01_concepts.md b/x/evidence/spec/01_concepts.md new file mode 100644 index 000000000000..b71f493f2dde --- /dev/null +++ b/x/evidence/spec/01_concepts.md @@ -0,0 +1,58 @@ +# Concepts + +## Evidence + +Any concrete type of evidence submitted to the `x/evidence` module must fulfill the +`Evidence` contract outlined below. Not all concrete types of evidence will fulfill +this contract in the same way and some data may be entirely irrelevant to certain +types of evidence. + +```go +type Evidence interface { + Route() string + Type() string + String() string + Hash() HexBytes + ValidateBasic() error + + // The consensus address of the malicious validator at time of infraction + GetConsensusAddress() ConsAddress + + // Height at which the infraction occurred + GetHeight() int64 + + // The total power of the malicious validator at time of infraction + GetValidatorPower() int64 + + // The total validator set power at time of infraction + GetTotalPower() int64 +} +``` + +## Registration & Handling + +The `x/evidence` module must first know about all types of evidence it is expected +to handle. This is accomplished by registering the `Route` method in the `Evidence` +contract with what is known as a `Router` (defined below). The `Router` accepts +`Evidence` and attempts to find the corresponding `Handler` for the `Evidence` +via the `Route` method. + +```go +type Router interface { + AddRoute(r string, h Handler) Router + HasRoute(r string) bool + GetRoute(path string) Handler + Seal() + Sealed() bool +} +``` + +The `Handler` (defined below) is responsible for executing the entirety of the +business logic for handling `Evidence`. This typically includes validating the +evidence, both stateless checks via `ValidateBasic` and stateful checks via any +keepers provided to the `Handler`. In addition, the `Handler` may also perform +capabilities such as slashing and jailing a validator. + +```go +type Handler func(Context, Evidence) error +``` diff --git a/x/evidence/spec/02_state.md b/x/evidence/spec/02_state.md new file mode 100644 index 000000000000..163743a7c829 --- /dev/null +++ b/x/evidence/spec/02_state.md @@ -0,0 +1,12 @@ +# State + +Currently the `x/evidence` module only stores valid submitted `Evidence` in state. +The evidence state is also stored and exported in the `x/evidence` module's `GenesisState`. + +```go +type GenesisState struct { + Evidence []Evidence `json:"evidence" yaml:"evidence"` +} +``` + +All `Evidence` is retrieved and stored via a prefix `KVStore` using prefix `0x00` (`KeyPrefixEvidence`). diff --git a/x/evidence/spec/03_messages.md b/x/evidence/spec/03_messages.md new file mode 100644 index 000000000000..84b8a844e951 --- /dev/null +++ b/x/evidence/spec/03_messages.md @@ -0,0 +1,42 @@ +# Messages + +## MsgSubmitEvidence + +Evidence is submitted through a `MsgSubmitEvidence` message: + +```go +type MsgSubmitEvidence struct { + Evidence Evidence + Submitter AccAddress +} +``` + +Note, the `Evidence` of a `MsgSubmitEvidence` message must have a corresponding +`Handler` registered with the `x/evidence` module's `Router` in order to be processed +and routed correctly. + +Given the `Evidence` is registered with a corresponding `Handler`, it is processed +as follows: + +```go +func SubmitEvidence(ctx Context, evidence Evidence) error { + if _, ok := GetEvidence(ctx, evidence.Hash()); ok { + return ErrEvidenceExists(codespace, evidence.Hash().String()) + } + if !router.HasRoute(evidence.Route()) { + return ErrNoEvidenceHandlerExists(codespace, evidence.Route()) + } + + handler := router.GetRoute(evidence.Route()) + if err := handler(ctx, evidence); err != nil { + return ErrInvalidEvidence(codespace, err.Error()) + } + + SetEvidence(ctx, evidence) + return nil +} +``` + +First, there must not already exist valid submitted `Evidence` of the exact same +type. Secondly, the `Evidence` is routed to the `Handler` and executed. Finally, +if there is no error in handling the `Evidence`, it is persisted to state. diff --git a/x/evidence/spec/README.md b/x/evidence/spec/README.md new file mode 100644 index 000000000000..de9f1ff91bba --- /dev/null +++ b/x/evidence/spec/README.md @@ -0,0 +1,27 @@ +# Evidence Module Specification + +## Abstract + +`x/evidence` is an implementation of a Cosmos SDK module, per [ADR 009](./../../../docs/architecture/adr-009-evidence-module.md), +that allows for the submission and handling of arbitrary evidence of misbehavior such +as equivocation and counterfactual signing. + +The evidence module differs from standard evidence handling which typically expects the +underlying consensus engine, e.g. Tendermint, to automatically submit evidence when +it is discovered by allowing clients and foreign chains to submit more complex evidence +directly. + +All concrete evidence types must implement the `Evidence` interface contract. Submitted +`Evidence` is first routed through the evidence module's `Router` in which it attempts +to find a corresponding registered `Handler` for that specific `Evidence` type. +Each `Evidence` type must have a `Handler` registered with the evidence module's +keeper in order for it to be successfully routed and executed. + +Each corresponding handler must also fulfill the `Handler` interface contract. The +`Handler` for a given `Evidence` type can perform any arbitrary state transitions +such as slashing, jailing, and tombstoning. + + +1. **[Concepts](01_concepts.md)** +2. **[State](02_state.md)** +3. **[Messages](03_messages.md)** From 946628d6b1cc9edb475ccabdb4050b4896848e98 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Thu, 31 Oct 2019 13:44:52 -0400 Subject: [PATCH 54/69] add changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ecef886d00a8..da95b8c6ab8e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -80,6 +80,7 @@ increased significantly due to modular `AnteHandler` support. Increase GasLimit ### Features +* (x/evidence) [\#5240](https://github.com/cosmos/cosmos-sdk/pull/5240) Initial implementation of the `x/evidence` module. * (cli) [\#5212](https://github.com/cosmos/cosmos-sdk/issues/5212) The `q gov proposals` command now supports pagination. * (store) [\#4724](https://github.com/cosmos/cosmos-sdk/issues/4724) Multistore supports substore migrations upon load. New `rootmulti.Store.LoadLatestVersionAndUpgrade` method in `Baseapp` supports `StoreLoader` to enable various upgrade strategies. It no From c829a3fc10ca785b0da1e0e97df206861f5fffa6 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Thu, 31 Oct 2019 13:55:04 -0400 Subject: [PATCH 55/69] update adr --- docs/architecture/adr-009-evidence-module.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/architecture/adr-009-evidence-module.md b/docs/architecture/adr-009-evidence-module.md index 0d4edfe5a80f..b7d0fdc49a5f 100644 --- a/docs/architecture/adr-009-evidence-module.md +++ b/docs/architecture/adr-009-evidence-module.md @@ -179,3 +179,4 @@ due to the inability to introduce the new evidence type's corresponding handler - [ICS](https://github.com/cosmos/ics) - [IBC Architecture](https://github.com/cosmos/ics/blob/master/ibc/1_IBC_ARCHITECTURE.md) +- [Tendermint Fork Accountability](https://github.com/tendermint/tendermint/blob/master/docs/spec/consensus/fork-accountability.md) From 829c6740f428835b005403d1e97ffb25a1136190 Mon Sep 17 00:00:00 2001 From: Alexander Bezobchuk Date: Tue, 5 Nov 2019 08:10:20 -0700 Subject: [PATCH 56/69] Update x/evidence/client/cli/query.go Co-Authored-By: Federico Kunze <31522760+fedekunze@users.noreply.github.com> --- x/evidence/client/cli/query.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/evidence/client/cli/query.go b/x/evidence/client/cli/query.go index 207c25dcf538..af5fdf84e7ca 100644 --- a/x/evidence/client/cli/query.go +++ b/x/evidence/client/cli/query.go @@ -50,7 +50,7 @@ $ %s query %s --page=2 --limit=50 // QueryEvidenceCMD returns the command handler for evidence querying. Evidence // can be queried for by hash or paginated evidence can be returned. -func QueryEvidenceCMD(cdc *codec.Codec) func(*cobra.Command, []string) error { +func QueryEvidenceCmd(cdc *codec.Codec) func(*cobra.Command, []string) error { return func(cmd *cobra.Command, args []string) error { if err := client.ValidateCmd(cmd, args); err != nil { return err From 2fc3737da0ffd7672f638b4c5721931370884702 Mon Sep 17 00:00:00 2001 From: Alexander Bezobchuk Date: Tue, 5 Nov 2019 08:10:30 -0700 Subject: [PATCH 57/69] Update x/evidence/client/cli/query.go Co-Authored-By: Federico Kunze <31522760+fedekunze@users.noreply.github.com> --- x/evidence/client/cli/query.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/evidence/client/cli/query.go b/x/evidence/client/cli/query.go index af5fdf84e7ca..4cd06a6f0e3b 100644 --- a/x/evidence/client/cli/query.go +++ b/x/evidence/client/cli/query.go @@ -48,7 +48,7 @@ $ %s query %s --page=2 --limit=50 return cmd } -// QueryEvidenceCMD returns the command handler for evidence querying. Evidence +// QueryEvidenceCmd returns the command handler for evidence querying. Evidence // can be queried for by hash or paginated evidence can be returned. func QueryEvidenceCmd(cdc *codec.Codec) func(*cobra.Command, []string) error { return func(cmd *cobra.Command, args []string) error { From 2d12a8431aa2e0e971bcff218e558b7e115a3ada Mon Sep 17 00:00:00 2001 From: Alexander Bezobchuk Date: Tue, 5 Nov 2019 08:10:39 -0700 Subject: [PATCH 58/69] Update x/evidence/client/cli/query.go Co-Authored-By: Federico Kunze <31522760+fedekunze@users.noreply.github.com> --- x/evidence/client/cli/query.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/evidence/client/cli/query.go b/x/evidence/client/cli/query.go index 4cd06a6f0e3b..e0d33af1ff81 100644 --- a/x/evidence/client/cli/query.go +++ b/x/evidence/client/cli/query.go @@ -39,7 +39,7 @@ $ %s query %s --page=2 --limit=50 Args: cobra.MaximumNArgs(1), DisableFlagParsing: true, SuggestionsMinimumDistance: 2, - RunE: QueryEvidenceCMD(cdc), + RunE: QueryEvidenceCmd(cdc), } cmd.Flags().Int(flagPage, 1, "pagination page of evidence to to query for") From bac71c0b6b78a9782fcf501f6a8212ca37cff673 Mon Sep 17 00:00:00 2001 From: Alexander Bezobchuk Date: Tue, 5 Nov 2019 08:11:35 -0700 Subject: [PATCH 59/69] Update x/evidence/client/cli/tx.go Co-Authored-By: Federico Kunze <31522760+fedekunze@users.noreply.github.com> --- x/evidence/client/cli/tx.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/evidence/client/cli/tx.go b/x/evidence/client/cli/tx.go index cd7aac6177d8..330a4c5879bf 100644 --- a/x/evidence/client/cli/tx.go +++ b/x/evidence/client/cli/tx.go @@ -22,7 +22,7 @@ func GetTxCmd(storeKey string, cdc *codec.Codec, childCmds []*cobra.Command) *co RunE: client.ValidateCmd, } - submitEvidenceCmd := SubmitEvidenceCMD(cdc) + submitEvidenceCmd := SubmitEvidenceCmd(cdc) for _, childCmd := range childCmds { submitEvidenceCmd.AddCommand(client.PostCommands(childCmd)[0]) } From ad7e945df0e27afb2f3b0bdb90fa121beae5c49c Mon Sep 17 00:00:00 2001 From: Alexander Bezobchuk Date: Tue, 5 Nov 2019 08:11:47 -0700 Subject: [PATCH 60/69] Update x/evidence/client/cli/tx.go Co-Authored-By: Federico Kunze <31522760+fedekunze@users.noreply.github.com> --- x/evidence/client/cli/tx.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/evidence/client/cli/tx.go b/x/evidence/client/cli/tx.go index 330a4c5879bf..846c90658080 100644 --- a/x/evidence/client/cli/tx.go +++ b/x/evidence/client/cli/tx.go @@ -32,7 +32,7 @@ func GetTxCmd(storeKey string, cdc *codec.Codec, childCmds []*cobra.Command) *co return cmd } -// SubmitEvidenceCMD returns the top-level evidence submission command handler. +// SubmitEvidenceCmd returns the top-level evidence submission command handler. // All concrete evidence submission child command handlers should be registered // under this command. func SubmitEvidenceCMD(cdc *codec.Codec) *cobra.Command { From 4f9eb5d19c0e885f9f2f8283a3a3da3ed68a37c1 Mon Sep 17 00:00:00 2001 From: Alexander Bezobchuk Date: Tue, 5 Nov 2019 08:11:59 -0700 Subject: [PATCH 61/69] Update x/evidence/client/cli/tx.go Co-Authored-By: Federico Kunze <31522760+fedekunze@users.noreply.github.com> --- x/evidence/client/cli/tx.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/evidence/client/cli/tx.go b/x/evidence/client/cli/tx.go index 846c90658080..7cf6c5f6eadb 100644 --- a/x/evidence/client/cli/tx.go +++ b/x/evidence/client/cli/tx.go @@ -35,7 +35,7 @@ func GetTxCmd(storeKey string, cdc *codec.Codec, childCmds []*cobra.Command) *co // SubmitEvidenceCmd returns the top-level evidence submission command handler. // All concrete evidence submission child command handlers should be registered // under this command. -func SubmitEvidenceCMD(cdc *codec.Codec) *cobra.Command { +func SubmitEvidenceCmd(cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ Use: "submit", Short: "Submit arbitrary evidence of misbehavior", From 0aebd9563df5dadb308a94e070a92acd7d397d89 Mon Sep 17 00:00:00 2001 From: Alexander Bezobchuk Date: Tue, 5 Nov 2019 08:13:10 -0700 Subject: [PATCH 62/69] Update x/evidence/client/rest/query.go Co-Authored-By: Federico Kunze <31522760+fedekunze@users.noreply.github.com> --- x/evidence/client/rest/query.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/evidence/client/rest/query.go b/x/evidence/client/rest/query.go index 050808f112dd..8c9e98f90a07 100644 --- a/x/evidence/client/rest/query.go +++ b/x/evidence/client/rest/query.go @@ -28,7 +28,7 @@ func queryEvidenceHandler(cliCtx context.CLIContext) http.HandlerFunc { vars := mux.Vars(r) evidenceHash := vars[RestParamEvidenceHash] - if len(evidenceHash) == 0 { + if strings.TrimSpace(evidenceHash) == "" { rest.WriteErrorResponse(w, http.StatusBadRequest, "evidence hash required but not specified") return } From 0737aa8aa2cd52754aa00c52f596e6130710d403 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Tue, 5 Nov 2019 10:20:29 -0500 Subject: [PATCH 63/69] fix build --- x/evidence/client/rest/query.go | 1 + x/evidence/internal/types/genesis.go | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/x/evidence/client/rest/query.go b/x/evidence/client/rest/query.go index 8c9e98f90a07..84a894487a99 100644 --- a/x/evidence/client/rest/query.go +++ b/x/evidence/client/rest/query.go @@ -3,6 +3,7 @@ package rest import ( "fmt" "net/http" + "strings" "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/types/rest" diff --git a/x/evidence/internal/types/genesis.go b/x/evidence/internal/types/genesis.go index 521ea9004248..2979f055dc65 100644 --- a/x/evidence/internal/types/genesis.go +++ b/x/evidence/internal/types/genesis.go @@ -1,6 +1,7 @@ -// DONTCOVER package types +// DONTCOVER + // GenesisState defines the evidence module's genesis state. type GenesisState struct { Evidence []Evidence `json:"evidence" yaml:"evidence"` From 6991768b501eaa17d3967be1438a0e14b76dc9b3 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Tue, 5 Nov 2019 10:22:06 -0500 Subject: [PATCH 64/69] remove abci.go --- x/evidence/abci.go | 1 - 1 file changed, 1 deletion(-) delete mode 100644 x/evidence/abci.go diff --git a/x/evidence/abci.go b/x/evidence/abci.go deleted file mode 100644 index fc422ea0ab90..000000000000 --- a/x/evidence/abci.go +++ /dev/null @@ -1 +0,0 @@ -package evidence From 0d5bd138f3a3dd299e9136b3bd1c50eab3a51145 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Tue, 5 Nov 2019 10:29:17 -0500 Subject: [PATCH 65/69] add to docs file --- docs/building-modules/README.md | 75 ++++++++++++++++----------------- 1 file changed, 36 insertions(+), 39 deletions(-) diff --git a/docs/building-modules/README.md b/docs/building-modules/README.md index 5b5743671751..145e6e501dfa 100644 --- a/docs/building-modules/README.md +++ b/docs/building-modules/README.md @@ -1,78 +1,75 @@ -# Auth +# Modules -The `x/auth` modules is used for accounts +## Auth -See the [API docs](https://godoc.org/github.com/cosmos/cosmos-sdk/x/auth) +The `x/auth` modules is used for accounts -See the [specification](https://github.com/cosmos/cosmos-sdk/tree/master/docs/spec/auth) +- [API docs](https://godoc.org/github.com/cosmos/cosmos-sdk/x/auth) +- [Specification](https://github.com/cosmos/cosmos-sdk/tree/master/docs/spec/auth) -# Bank +## Bank The `x/bank` module is for transferring coins between accounts. -See the [API docs](https://godoc.org/github.com/cosmos/cosmos-sdk/x/bank). - -See the [specification](https://github.com/cosmos/cosmos-sdk/tree/master/docs/spec/bank) +- [API docs](https://godoc.org/github.com/cosmos/cosmos-sdk/x/bank) +- [Specification](https://github.com/cosmos/cosmos-sdk/tree/master/docs/spec/bank) -# Stake +## Staking The `x/staking` module is for Cosmos Delegated-Proof-of-Stake. -See the [API docs](https://godoc.org/github.com/cosmos/cosmos-sdk/x/staking). +- [API docs](https://godoc.org/github.com/cosmos/cosmos-sdk/x/staking) +- [Specification](https://github.com/cosmos/cosmos-sdk/tree/master/docs/spec/staking) -See the -[specification](https://github.com/cosmos/cosmos-sdk/tree/master/docs/spec/staking) - -# Slashing +## Slashing The `x/slashing` module is for Cosmos Delegated-Proof-of-Stake. -See the [API docs](https://godoc.org/github.com/cosmos/cosmos-sdk/x/slashing) - -See the -[specification](https://github.com/cosmos/cosmos-sdk/tree/master/docs/spec/slashing) +- [API docs](https://godoc.org/github.com/cosmos/cosmos-sdk/x/slashing) +- [Specification](https://github.com/cosmos/cosmos-sdk/tree/master/docs/spec/slashing) -# Distribution +## Distribution The `x/distribution` module is for distributing fees and inflation across bonded stakeholders. -See the [API docs](https://godoc.org/github.com/cosmos/cosmos-sdk/x/distribution) - -See the -[specification](https://github.com/cosmos/cosmos-sdk/tree/master/docs/spec/distribution) +- [API docs](https://godoc.org/github.com/cosmos/cosmos-sdk/x/distribution) +- [Specification](https://github.com/cosmos/cosmos-sdk/tree/master/docs/spec/distribution) -# Governance +## Governance The `x/gov` module is for bonded stakeholders to make proposals and vote on them. -See the [API docs](https://godoc.org/github.com/cosmos/cosmos-sdk/x/gov) - -See the -[specification](https://github.com/cosmos/cosmos-sdk/tree/master/docs/spec/governance) +- [API docs](https://godoc.org/github.com/cosmos/cosmos-sdk/x/gov) +- [Specification](https://github.com/cosmos/cosmos-sdk/tree/master/docs/spec/governance) To keep up with the current status of IBC, follow and contribute to [ICS](https://github.com/cosmos/ics) -# Crisis +## Crisis The `x/crisis` module is for halting the blockchain under certain circumstances. -See the [API Docs](https://godoc.org/github.com/cosmos/cosmos-sdk/x/crisis) - -See the [specification](https://github.com/cosmos/cosmos-sdk/blob/master/docs/spec/crisis) +- [API Docs](https://godoc.org/github.com/cosmos/cosmos-sdk/x/crisis) +- [Specification](https://github.com/cosmos/cosmos-sdk/blob/master/docs/spec/crisis) -# Mint +## Mint The `x/mint` module is for flexible inflation rates and effect a balance between market liquidity and staked supply. -See the [API Docs](https://godoc.org/github.com/cosmos/cosmos-sdk/x/mint) - -See the [specification](https://github.com/cosmos/cosmos-sdk/blob/master/docs/spec/mint) +- [API Docs](https://godoc.org/github.com/cosmos/cosmos-sdk/x/mint) +- [Specification](https://github.com/cosmos/cosmos-sdk/blob/master/docs/spec/mint) -# Params +## Params The `x/params` module provides a globally available parameter store. -See the [API Docs](https://godoc.org/github.com/cosmos/cosmos-sdk/x/params) +- [API Docs](https://godoc.org/github.com/cosmos/cosmos-sdk/x/params) +- [Specification](https://github.com/cosmos/cosmos-sdk/blob/master/docs/spec/params) + +## Evidence + +The `x/evidence` modules provides a mechanism for defining and submitting arbitrary +events of misbehavior and a means to execute custom business logic for such misbehavior. -See the [specification](https://github.com/cosmos/cosmos-sdk/blob/master/docs/spec/params) +- [API Docs](https://godoc.org/github.com/cosmos/cosmos-sdk/x/evidence) +- [Specification](https://github.com/cosmos/cosmos-sdk/blob/master/docs/spec/evidence) From 1b4615006deae3a29962501565e63581e53140cb Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Tue, 5 Nov 2019 10:38:09 -0500 Subject: [PATCH 66/69] move Evidence type to exported --- x/evidence/alias.go | 1 - x/evidence/client/cli/query.go | 5 +++-- .../{internal/types => exported}/evidence.go | 2 +- x/evidence/internal/keeper/keeper.go | 15 ++++++++------- x/evidence/internal/keeper/keeper_test.go | 5 +++-- x/evidence/internal/keeper/querier.go | 3 ++- x/evidence/internal/keeper/querier_test.go | 7 ++++--- x/evidence/internal/types/codec.go | 3 ++- x/evidence/internal/types/genesis.go | 8 +++++--- x/evidence/internal/types/msgs.go | 7 ++++--- x/evidence/internal/types/router.go | 3 ++- x/evidence/internal/types/test_util.go | 5 +++-- 12 files changed, 37 insertions(+), 27 deletions(-) rename x/evidence/{internal/types => exported}/evidence.go (97%) diff --git a/x/evidence/alias.go b/x/evidence/alias.go index 8f2d2bdb8737..6c7fa81519ea 100644 --- a/x/evidence/alias.go +++ b/x/evidence/alias.go @@ -42,7 +42,6 @@ type ( GenesisState = types.GenesisState MsgSubmitEvidence = types.MsgSubmitEvidence - Evidence = types.Evidence Handler = types.Handler Router = types.Router ) diff --git a/x/evidence/client/cli/query.go b/x/evidence/client/cli/query.go index e0d33af1ff81..0529af859411 100644 --- a/x/evidence/client/cli/query.go +++ b/x/evidence/client/cli/query.go @@ -12,6 +12,7 @@ import ( "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/version" + "github.com/cosmos/cosmos-sdk/x/evidence/exported" "github.com/cosmos/cosmos-sdk/x/evidence/internal/types" ) @@ -83,7 +84,7 @@ func queryEvidence(cdc *codec.Codec, cliCtx context.CLIContext, hash string) err return err } - var evidence types.Evidence + var evidence exported.Evidence err = cdc.UnmarshalJSON(res, &evidence) if err != nil { return fmt.Errorf("failed to unmarshal evidence: %w", err) @@ -105,7 +106,7 @@ func queryAllEvidence(cdc *codec.Codec, cliCtx context.CLIContext) error { return err } - var evidence []types.Evidence + var evidence []exported.Evidence err = cdc.UnmarshalJSON(res, &evidence) if err != nil { return fmt.Errorf("failed to unmarshal evidence: %w", err) diff --git a/x/evidence/internal/types/evidence.go b/x/evidence/exported/evidence.go similarity index 97% rename from x/evidence/internal/types/evidence.go rename to x/evidence/exported/evidence.go index 4e9c55d874fc..1c6adfdf8c0a 100644 --- a/x/evidence/internal/types/evidence.go +++ b/x/evidence/exported/evidence.go @@ -1,4 +1,4 @@ -package types +package exported import ( sdk "github.com/cosmos/cosmos-sdk/types" diff --git a/x/evidence/internal/keeper/keeper.go b/x/evidence/internal/keeper/keeper.go index c2da04194931..ca4e3e08f7b1 100644 --- a/x/evidence/internal/keeper/keeper.go +++ b/x/evidence/internal/keeper/keeper.go @@ -9,6 +9,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/evidence/exported" "github.com/cosmos/cosmos-sdk/x/evidence/internal/types" "github.com/cosmos/cosmos-sdk/x/params" ) @@ -63,7 +64,7 @@ func (k *Keeper) SetRouter(rtr types.Router) { // 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 types.Evidence) error { +func (k Keeper) SubmitEvidence(ctx sdk.Context, evidence exported.Evidence) error { if _, ok := k.GetEvidence(ctx, evidence.Hash()); ok { return types.ErrEvidenceExists(k.codespace, evidence.Hash().String()) } @@ -81,7 +82,7 @@ func (k Keeper) SubmitEvidence(ctx sdk.Context, evidence types.Evidence) error { } // SetEvidence sets Evidence by hash in the module's KVStore. -func (k Keeper) SetEvidence(ctx sdk.Context, evidence types.Evidence) { +func (k Keeper) SetEvidence(ctx sdk.Context, evidence exported.Evidence) { store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixEvidence) bz := k.cdc.MustMarshalBinaryLengthPrefixed(evidence) store.Set(evidence.Hash(), bz) @@ -89,7 +90,7 @@ func (k Keeper) SetEvidence(ctx sdk.Context, evidence types.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 cmn.HexBytes) (evidence types.Evidence, found bool) { +func (k Keeper) GetEvidence(ctx sdk.Context, hash cmn.HexBytes) (evidence exported.Evidence, found bool) { store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixEvidence) bz := store.Get(hash) @@ -104,13 +105,13 @@ func (k Keeper) GetEvidence(ctx sdk.Context, hash cmn.HexBytes) (evidence types. // 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(types.Evidence) bool) { +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() { - var evidence types.Evidence + var evidence exported.Evidence k.cdc.MustUnmarshalBinaryLengthPrefixed(iterator.Value(), &evidence) if cb(evidence) { @@ -120,8 +121,8 @@ func (k Keeper) IterateEvidence(ctx sdk.Context, cb func(types.Evidence) bool) { } // GetAllEvidence returns all stored Evidence objects. -func (k Keeper) GetAllEvidence(ctx sdk.Context) (evidence []types.Evidence) { - k.IterateEvidence(ctx, func(e types.Evidence) bool { +func (k Keeper) GetAllEvidence(ctx sdk.Context) (evidence []exported.Evidence) { + k.IterateEvidence(ctx, func(e exported.Evidence) bool { evidence = append(evidence, e) return false }) diff --git a/x/evidence/internal/keeper/keeper_test.go b/x/evidence/internal/keeper/keeper_test.go index 310a8d3ef09e..4211a438c511 100644 --- a/x/evidence/internal/keeper/keeper_test.go +++ b/x/evidence/internal/keeper/keeper_test.go @@ -6,6 +6,7 @@ import ( "github.com/cosmos/cosmos-sdk/simapp" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/evidence" + "github.com/cosmos/cosmos-sdk/x/evidence/exported" "github.com/cosmos/cosmos-sdk/x/evidence/internal/keeper" "github.com/cosmos/cosmos-sdk/x/evidence/internal/types" @@ -44,8 +45,8 @@ func (suite *KeeperTestSuite) SetupTest() { suite.keeper = *evidenceKeeper } -func (suite *KeeperTestSuite) populateEvidence(ctx sdk.Context, numEvidence int) []types.Evidence { - evidence := make([]types.Evidence, numEvidence) +func (suite *KeeperTestSuite) populateEvidence(ctx sdk.Context, numEvidence int) []exported.Evidence { + evidence := make([]exported.Evidence, numEvidence) for i := 0; i < numEvidence; i++ { pk := ed25519.GenPrivKey() diff --git a/x/evidence/internal/keeper/querier.go b/x/evidence/internal/keeper/querier.go index 97d205678a0b..fa6ef17c0f31 100644 --- a/x/evidence/internal/keeper/querier.go +++ b/x/evidence/internal/keeper/querier.go @@ -7,6 +7,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" 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/internal/types" abci "github.com/tendermint/tendermint/abci/types" @@ -72,7 +73,7 @@ func queryAllEvidence(ctx sdk.Context, _ []string, req abci.RequestQuery, k Keep start, end := client.Paginate(len(evidence), params.Page, params.Limit, 100) if start < 0 || end < 0 { - evidence = []types.Evidence{} + evidence = []exported.Evidence{} } else { evidence = evidence[start:end] } diff --git a/x/evidence/internal/keeper/querier_test.go b/x/evidence/internal/keeper/querier_test.go index e3dabb0c14f6..b656019edd8a 100644 --- a/x/evidence/internal/keeper/querier_test.go +++ b/x/evidence/internal/keeper/querier_test.go @@ -3,6 +3,7 @@ package keeper_test import ( "strings" + "github.com/cosmos/cosmos-sdk/x/evidence/exported" "github.com/cosmos/cosmos-sdk/x/evidence/internal/types" abci "github.com/tendermint/tendermint/abci/types" @@ -26,7 +27,7 @@ func (suite *KeeperTestSuite) TestQueryEvidence_Existing() { suite.Nil(err) suite.NotNil(bz) - var e types.Evidence + var e exported.Evidence suite.Nil(types.TestingCdc.UnmarshalJSON(bz, &e)) suite.Equal(evidence[0], e) } @@ -60,7 +61,7 @@ func (suite *KeeperTestSuite) TestQueryAllEvidence() { suite.Nil(err) suite.NotNil(bz) - var e []types.Evidence + var e []exported.Evidence suite.Nil(types.TestingCdc.UnmarshalJSON(bz, &e)) suite.Len(e, numEvidence) } @@ -79,7 +80,7 @@ func (suite *KeeperTestSuite) TestQueryAllEvidence_InvalidPagination() { suite.Nil(err) suite.NotNil(bz) - var e []types.Evidence + var e []exported.Evidence suite.Nil(types.TestingCdc.UnmarshalJSON(bz, &e)) suite.Len(e, 0) } diff --git a/x/evidence/internal/types/codec.go b/x/evidence/internal/types/codec.go index fd416db5e680..72fb044e9d55 100644 --- a/x/evidence/internal/types/codec.go +++ b/x/evidence/internal/types/codec.go @@ -2,6 +2,7 @@ package types import ( "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/x/evidence/exported" ) // ModuleCdc defines the evidence module's codec. The codec is not sealed as to @@ -11,7 +12,7 @@ var ModuleCdc = codec.New() // RegisterCodec registers all the necessary types and interfaces for the // evidence module. func RegisterCodec(cdc *codec.Codec) { - cdc.RegisterInterface((*Evidence)(nil), nil) + cdc.RegisterInterface((*exported.Evidence)(nil), nil) cdc.RegisterConcrete(MsgSubmitEvidence{}, "cosmos-sdk/MsgSubmitEvidence", nil) } diff --git a/x/evidence/internal/types/genesis.go b/x/evidence/internal/types/genesis.go index 2979f055dc65..4013b573fc9f 100644 --- a/x/evidence/internal/types/genesis.go +++ b/x/evidence/internal/types/genesis.go @@ -1,19 +1,21 @@ package types +import "github.com/cosmos/cosmos-sdk/x/evidence/exported" + // DONTCOVER // GenesisState defines the evidence module's genesis state. type GenesisState struct { - Evidence []Evidence `json:"evidence" yaml:"evidence"` + Evidence []exported.Evidence `json:"evidence" yaml:"evidence"` } -func NewGenesisState(e []Evidence) GenesisState { +func NewGenesisState(e []exported.Evidence) GenesisState { return GenesisState{Evidence: e} } // DefaultGenesisState returns the evidence module's default genesis state. func DefaultGenesisState() GenesisState { - return GenesisState{Evidence: []Evidence{}} + return GenesisState{Evidence: []exported.Evidence{}} } // Validate performs basic gensis state validation returning an error upon any diff --git a/x/evidence/internal/types/msgs.go b/x/evidence/internal/types/msgs.go index 12a8e5cddea3..b09f120da927 100644 --- a/x/evidence/internal/types/msgs.go +++ b/x/evidence/internal/types/msgs.go @@ -3,6 +3,7 @@ package types import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/x/evidence/exported" ) // Message types for the evidence module @@ -17,11 +18,11 @@ var ( // MsgSubmitEvidence defines an sdk.Msg type that supports submitting arbitrary // Evidence. type MsgSubmitEvidence struct { - Evidence Evidence `json:"evidence" yaml:"evidence"` - Submitter sdk.AccAddress `json:"submitter" yaml:"submitter"` + Evidence exported.Evidence `json:"evidence" yaml:"evidence"` + Submitter sdk.AccAddress `json:"submitter" yaml:"submitter"` } -func NewMsgSubmitEvidence(e Evidence, s sdk.AccAddress) MsgSubmitEvidence { +func NewMsgSubmitEvidence(e exported.Evidence, s sdk.AccAddress) MsgSubmitEvidence { return MsgSubmitEvidence{Evidence: e, Submitter: s} } diff --git a/x/evidence/internal/types/router.go b/x/evidence/internal/types/router.go index 2d99a0f0a5d3..343c72e41de5 100644 --- a/x/evidence/internal/types/router.go +++ b/x/evidence/internal/types/router.go @@ -4,6 +4,7 @@ import ( "fmt" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/evidence/exported" ) type ( @@ -11,7 +12,7 @@ type ( // for executing all corresponding business logic necessary for verifying the // evidence as valid. In addition, the Handler may execute any necessary // slashing and potential jailing. - Handler func(sdk.Context, Evidence) error + Handler func(sdk.Context, exported.Evidence) error // Router defines a contract for which any Evidence handling module must // implement in order to route Evidence to registered Handlers. diff --git a/x/evidence/internal/types/test_util.go b/x/evidence/internal/types/test_util.go index 98a3e706781a..b138a352b971 100644 --- a/x/evidence/internal/types/test_util.go +++ b/x/evidence/internal/types/test_util.go @@ -13,6 +13,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/evidence/exported" "gopkg.in/yaml.v2" @@ -22,7 +23,7 @@ import ( ) var ( - _ Evidence = (*TestEquivocationEvidence)(nil) + _ exported.Evidence = (*TestEquivocationEvidence)(nil) TestingCdc = codec.New() ) @@ -111,7 +112,7 @@ func (v TestVote) SignBytes(chainID string) []byte { } func TestEquivocationHandler(k interface{}) Handler { - return func(ctx sdk.Context, e Evidence) error { + return func(ctx sdk.Context, e exported.Evidence) error { if err := e.ValidateBasic(); err != nil { return err } From 709d5f5016b38bdb1218ce163811a0d26a01bbcb Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Tue, 5 Nov 2019 10:52:51 -0500 Subject: [PATCH 67/69] emit events for evidence submission --- x/evidence/alias.go | 3 +++ x/evidence/handler.go | 8 ++++++++ x/evidence/internal/keeper/keeper.go | 7 +++++++ x/evidence/internal/types/events.go | 9 +++++++++ x/evidence/spec/04_events.md | 14 ++++++++++++++ x/evidence/spec/README.md | 1 + 6 files changed, 42 insertions(+) create mode 100644 x/evidence/internal/types/events.go create mode 100644 x/evidence/spec/04_events.md diff --git a/x/evidence/alias.go b/x/evidence/alias.go index 6c7fa81519ea..5eca5a074f1f 100644 --- a/x/evidence/alias.go +++ b/x/evidence/alias.go @@ -20,6 +20,9 @@ const ( CodeNoEvidenceExists = types.CodeNoEvidenceExists TypeMsgSubmitEvidence = types.TypeMsgSubmitEvidence DefaultCodespace = types.DefaultCodespace + EventTypeSubmitEvidence = types.EventTypeSubmitEvidence + AttributeValueCategory = types.AttributeValueCategory + AttributeKeyEvidenceHash = types.AttributeKeyEvidenceHash ) var ( diff --git a/x/evidence/handler.go b/x/evidence/handler.go index b2882bae60c0..714da444c444 100644 --- a/x/evidence/handler.go +++ b/x/evidence/handler.go @@ -25,6 +25,14 @@ func handleMsgSubmitEvidence(ctx sdk.Context, k Keeper, msg MsgSubmitEvidence) s return sdk.ConvertError(err).Result() } + ctx.EventManager().EmitEvent( + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, AttributeValueCategory), + sdk.NewAttribute(sdk.AttributeKeySender, msg.Submitter.String()), + ), + ) + return sdk.Result{ Data: msg.Evidence.Hash(), Events: ctx.EventManager().Events(), diff --git a/x/evidence/internal/keeper/keeper.go b/x/evidence/internal/keeper/keeper.go index ca4e3e08f7b1..2bf34276b137 100644 --- a/x/evidence/internal/keeper/keeper.go +++ b/x/evidence/internal/keeper/keeper.go @@ -77,6 +77,13 @@ func (k Keeper) SubmitEvidence(ctx sdk.Context, evidence exported.Evidence) erro return types.ErrInvalidEvidence(k.codespace, err.Error()) } + ctx.EventManager().EmitEvent( + sdk.NewEvent( + types.EventTypeSubmitEvidence, + sdk.NewAttribute(types.AttributeKeyEvidenceHash, evidence.Hash().String()), + ), + ) + k.SetEvidence(ctx, evidence) return nil } diff --git a/x/evidence/internal/types/events.go b/x/evidence/internal/types/events.go new file mode 100644 index 000000000000..fe468c43e592 --- /dev/null +++ b/x/evidence/internal/types/events.go @@ -0,0 +1,9 @@ +package types + +// evidence module events +const ( + EventTypeSubmitEvidence = "submit_evidence" + + AttributeValueCategory = "evidence" + AttributeKeyEvidenceHash = "evidence_hash" +) diff --git a/x/evidence/spec/04_events.md b/x/evidence/spec/04_events.md new file mode 100644 index 000000000000..1994f2398f6a --- /dev/null +++ b/x/evidence/spec/04_events.md @@ -0,0 +1,14 @@ +# Events + +The `x/evidence` module emits the following events: + +## Handlers + +### MsgSubmitEvidence + +| Type | Attribute Key | Attribute Value | +| --------------- | ------------- | --------------- | +| submit_evidence | evidence_hash | {evidenceHash} | +| message | module | evidence | +| message | sender | {senderAddress} | +| message | action | submit_evidence | diff --git a/x/evidence/spec/README.md b/x/evidence/spec/README.md index de9f1ff91bba..1f8667b1b7f0 100644 --- a/x/evidence/spec/README.md +++ b/x/evidence/spec/README.md @@ -25,3 +25,4 @@ such as slashing, jailing, and tombstoning. 1. **[Concepts](01_concepts.md)** 2. **[State](02_state.md)** 3. **[Messages](03_messages.md)** +4. **[Events](04_events.md)** From cffa3b15ce306ea007d15ba9aa364795ccd0e275 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Tue, 5 Nov 2019 13:12:37 -0500 Subject: [PATCH 68/69] fix tests; goimports --- x/evidence/genesis_test.go | 10 ++++++---- x/evidence/internal/types/codec_test.go | 10 ++++++---- x/evidence/internal/types/genesis_test.go | 8 +++++--- x/evidence/internal/types/msgs_test.go | 3 ++- x/evidence/internal/types/router_test.go | 6 ++++-- 5 files changed, 23 insertions(+), 14 deletions(-) diff --git a/x/evidence/genesis_test.go b/x/evidence/genesis_test.go index 6a5e3fb2b87e..3f3e3abac48b 100644 --- a/x/evidence/genesis_test.go +++ b/x/evidence/genesis_test.go @@ -3,12 +3,14 @@ package evidence_test import ( "testing" + abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/crypto/ed25519" + "github.com/cosmos/cosmos-sdk/simapp" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/evidence" + "github.com/cosmos/cosmos-sdk/x/evidence/exported" "github.com/cosmos/cosmos-sdk/x/evidence/internal/types" - abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/crypto/ed25519" "github.com/stretchr/testify/suite" ) @@ -44,7 +46,7 @@ func (suite *GenesisTestSuite) SetupTest() { func (suite *GenesisTestSuite) TestInitGenesis_Valid() { pk := ed25519.GenPrivKey() - testEvidence := make([]types.Evidence, 100) + testEvidence := make([]exported.Evidence, 100) for i := 0; i < 100; i++ { sv := types.TestVote{ ValidatorAddress: pk.PubKey().Address(), @@ -77,7 +79,7 @@ func (suite *GenesisTestSuite) TestInitGenesis_Valid() { func (suite *GenesisTestSuite) TestInitGenesis_Invalid() { pk := ed25519.GenPrivKey() - testEvidence := make([]types.Evidence, 100) + testEvidence := make([]exported.Evidence, 100) for i := 0; i < 100; i++ { sv := types.TestVote{ ValidatorAddress: pk.PubKey().Address(), diff --git a/x/evidence/internal/types/codec_test.go b/x/evidence/internal/types/codec_test.go index c7042ee785c6..1edf6d97cc43 100644 --- a/x/evidence/internal/types/codec_test.go +++ b/x/evidence/internal/types/codec_test.go @@ -3,14 +3,16 @@ package types_test import ( "testing" + "github.com/stretchr/testify/require" + cmn "github.com/tendermint/tendermint/libs/common" + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/evidence/exported" "github.com/cosmos/cosmos-sdk/x/evidence/internal/types" - "github.com/stretchr/testify/require" - cmn "github.com/tendermint/tendermint/libs/common" ) -var _ types.Evidence = (*testEvidence)(nil) +var _ exported.Evidence = (*testEvidence)(nil) type testEvidence struct{} @@ -29,7 +31,7 @@ func TestCodec(t *testing.T) { types.RegisterCodec(cdc) types.RegisterEvidenceTypeCodec(testEvidence{}, "cosmos-sdk/testEvidence") - var e types.Evidence = testEvidence{} + var e exported.Evidence = testEvidence{} bz, err := cdc.MarshalBinaryBare(e) require.NoError(t, err) diff --git a/x/evidence/internal/types/genesis_test.go b/x/evidence/internal/types/genesis_test.go index 2a3d93edde09..936877ad3c2a 100644 --- a/x/evidence/internal/types/genesis_test.go +++ b/x/evidence/internal/types/genesis_test.go @@ -3,9 +3,11 @@ package types_test import ( "testing" - "github.com/cosmos/cosmos-sdk/x/evidence/internal/types" "github.com/stretchr/testify/require" "github.com/tendermint/tendermint/crypto/ed25519" + + "github.com/cosmos/cosmos-sdk/x/evidence/exported" + "github.com/cosmos/cosmos-sdk/x/evidence/internal/types" ) func TestDefaultGenesisState(t *testing.T) { @@ -17,7 +19,7 @@ func TestDefaultGenesisState(t *testing.T) { func TestGenesisStateValidate_Valid(t *testing.T) { pk := ed25519.GenPrivKey() - evidence := make([]types.Evidence, 100) + evidence := make([]exported.Evidence, 100) for i := 0; i < 100; i++ { sv := types.TestVote{ ValidatorAddress: pk.PubKey().Address(), @@ -44,7 +46,7 @@ func TestGenesisStateValidate_Valid(t *testing.T) { func TestGenesisStateValidate_Invalid(t *testing.T) { pk := ed25519.GenPrivKey() - evidence := make([]types.Evidence, 100) + evidence := make([]exported.Evidence, 100) for i := 0; i < 100; i++ { sv := types.TestVote{ ValidatorAddress: pk.PubKey().Address(), diff --git a/x/evidence/internal/types/msgs_test.go b/x/evidence/internal/types/msgs_test.go index 5d4e65943446..5a50ba6e7b2a 100644 --- a/x/evidence/internal/types/msgs_test.go +++ b/x/evidence/internal/types/msgs_test.go @@ -4,6 +4,7 @@ import ( "testing" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/evidence/exported" "github.com/cosmos/cosmos-sdk/x/evidence/internal/types" "github.com/stretchr/testify/require" @@ -23,7 +24,7 @@ func TestMsgSubmitEvidence(t *testing.T) { submitter := sdk.AccAddress("test") testCases := []struct { - evidence types.Evidence + evidence exported.Evidence submitter sdk.AccAddress expectErr bool }{ diff --git a/x/evidence/internal/types/router_test.go b/x/evidence/internal/types/router_test.go index fc94ad04ea9a..b8cb42d2d488 100644 --- a/x/evidence/internal/types/router_test.go +++ b/x/evidence/internal/types/router_test.go @@ -3,12 +3,14 @@ package types_test import ( "testing" + "github.com/stretchr/testify/require" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/evidence/exported" "github.com/cosmos/cosmos-sdk/x/evidence/internal/types" - "github.com/stretchr/testify/require" ) -func testHandler(sdk.Context, types.Evidence) error { return nil } +func testHandler(sdk.Context, exported.Evidence) error { return nil } func TestRouterSeal(t *testing.T) { r := types.NewRouter() From 3219e6457b0d30c3d648fd8f41fb49ddb7a19772 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 6 Nov 2019 14:59:47 -0500 Subject: [PATCH 69/69] add GetEvidenceHandler --- x/evidence/internal/keeper/keeper.go | 10 ++++++++++ x/evidence/internal/keeper/keeper_test.go | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/x/evidence/internal/keeper/keeper.go b/x/evidence/internal/keeper/keeper.go index 2bf34276b137..fa79aac7be08 100644 --- a/x/evidence/internal/keeper/keeper.go +++ b/x/evidence/internal/keeper/keeper.go @@ -60,6 +60,16 @@ func (k *Keeper) SetRouter(rtr types.Router) { 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, types.ErrNoEvidenceHandlerExists(k.codespace, 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 diff --git a/x/evidence/internal/keeper/keeper_test.go b/x/evidence/internal/keeper/keeper_test.go index 4211a438c511..2f7862283ae8 100644 --- a/x/evidence/internal/keeper/keeper_test.go +++ b/x/evidence/internal/keeper/keeper_test.go @@ -166,6 +166,16 @@ func (suite *KeeperTestSuite) TestIterateEvidence() { suite.Len(evidence, numEvidence) } +func (suite *KeeperTestSuite) TestGetEvidenceHandler() { + handler, err := suite.keeper.GetEvidenceHandler(types.TestEquivocationEvidence{}.Route()) + suite.NoError(err) + suite.NotNil(handler) + + handler, err = suite.keeper.GetEvidenceHandler("invalidHandler") + suite.Error(err) + suite.Nil(handler) +} + func TestKeeperTestSuite(t *testing.T) { suite.Run(t, new(KeeperTestSuite)) }