diff --git a/app/app.go b/app/app.go index 05309c5cfe..b7c3f5fccd 100644 --- a/app/app.go +++ b/app/app.go @@ -6,6 +6,7 @@ import ( "net/http" "os" + "github.com/CosmWasm/wasmd/x/wasm" "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/grpc/tmservice" @@ -16,6 +17,7 @@ import ( "github.com/cosmos/cosmos-sdk/server/config" servertypes "github.com/cosmos/cosmos-sdk/server/types" simappparams "github.com/cosmos/cosmos-sdk/simapp/params" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" "github.com/cosmos/cosmos-sdk/version" "github.com/cosmos/cosmos-sdk/x/auth" @@ -23,22 +25,32 @@ import ( authrest "github.com/cosmos/cosmos-sdk/x/auth/client/rest" authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" authsims "github.com/cosmos/cosmos-sdk/x/auth/simulation" + authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" "github.com/cosmos/cosmos-sdk/x/auth/vesting" "github.com/cosmos/cosmos-sdk/x/bank" bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" "github.com/cosmos/cosmos-sdk/x/capability" capabilitykeeper "github.com/cosmos/cosmos-sdk/x/capability/keeper" + capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" "github.com/cosmos/cosmos-sdk/x/crisis" crisiskeeper "github.com/cosmos/cosmos-sdk/x/crisis/keeper" + crisistypes "github.com/cosmos/cosmos-sdk/x/crisis/types" distr "github.com/cosmos/cosmos-sdk/x/distribution" distrkeeper "github.com/cosmos/cosmos-sdk/x/distribution/keeper" + distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" "github.com/cosmos/cosmos-sdk/x/evidence" evidencekeeper "github.com/cosmos/cosmos-sdk/x/evidence/keeper" + evidencetypes "github.com/cosmos/cosmos-sdk/x/evidence/types" "github.com/cosmos/cosmos-sdk/x/genutil" + genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" "github.com/cosmos/cosmos-sdk/x/gov" govkeeper "github.com/cosmos/cosmos-sdk/x/gov/keeper" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" "github.com/cosmos/cosmos-sdk/x/ibc/applications/transfer" ibctransferkeeper "github.com/cosmos/cosmos-sdk/x/ibc/applications/transfer/keeper" + ibctransfertypes "github.com/cosmos/cosmos-sdk/x/ibc/applications/transfer/types" ibc "github.com/cosmos/cosmos-sdk/x/ibc/core" ibcclient "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client" porttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/05-port/types" @@ -47,18 +59,22 @@ import ( ibcmock "github.com/cosmos/cosmos-sdk/x/ibc/testing/mock" "github.com/cosmos/cosmos-sdk/x/mint" mintkeeper "github.com/cosmos/cosmos-sdk/x/mint/keeper" + minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" "github.com/cosmos/cosmos-sdk/x/params" paramskeeper "github.com/cosmos/cosmos-sdk/x/params/keeper" + paramstypes "github.com/cosmos/cosmos-sdk/x/params/types" paramproposal "github.com/cosmos/cosmos-sdk/x/params/types/proposal" "github.com/cosmos/cosmos-sdk/x/slashing" slashingkeeper "github.com/cosmos/cosmos-sdk/x/slashing/keeper" + slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" "github.com/cosmos/cosmos-sdk/x/staking" stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "github.com/cosmos/cosmos-sdk/x/upgrade" upgradekeeper "github.com/cosmos/cosmos-sdk/x/upgrade/keeper" + upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" "github.com/gorilla/mux" "github.com/rakyll/statik/fs" - "github.com/regen-network/regen-ledger/types/module/server" "github.com/spf13/cast" abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/libs/log" @@ -66,25 +82,7 @@ import ( tmproto "github.com/tendermint/tendermint/proto/tendermint/types" dbm "github.com/tendermint/tm-db" - // types - sdk "github.com/cosmos/cosmos-sdk/types" - authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" - capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" - crisistypes "github.com/cosmos/cosmos-sdk/x/crisis/types" - distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" - evidencetypes "github.com/cosmos/cosmos-sdk/x/evidence/types" - genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" - govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" - ibctransfertypes "github.com/cosmos/cosmos-sdk/x/ibc/applications/transfer/types" - minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" - paramstypes "github.com/cosmos/cosmos-sdk/x/params/types" - slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" - - "github.com/CosmWasm/wasmd/x/wasm" + "github.com/regen-network/regen-ledger/types/module/server" ) const ( @@ -320,6 +318,7 @@ func NewRegenApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest // register experimental modules here app.smm = setCustomModules(app, interfaceRegistry) + app.smm.RegisterInvariants(&app.CrisisKeeper) var skipGenesisInvariants = cast.ToBool(appOpts.Get(crisis.FlagSkipGenesisInvariants)) diff --git a/app/experimental_appconfig.go b/app/experimental_appconfig.go index 20c56567ce..72988651b3 100644 --- a/app/experimental_appconfig.go +++ b/app/experimental_appconfig.go @@ -12,14 +12,14 @@ import ( "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" - govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" - upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" - distrclient "github.com/cosmos/cosmos-sdk/x/distribution/client" "github.com/cosmos/cosmos-sdk/x/gov" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" paramsclient "github.com/cosmos/cosmos-sdk/x/params/client" paramskeeper "github.com/cosmos/cosmos-sdk/x/params/keeper" upgradeclient "github.com/cosmos/cosmos-sdk/x/upgrade/client" + upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" + moduletypes "github.com/regen-network/regen-ledger/types/module" "github.com/regen-network/regen-ledger/types/module/server" data "github.com/regen-network/regen-ledger/x/data/module" diff --git a/app/stable_appconfig.go b/app/stable_appconfig.go index f756067b21..7c042ce8a1 100644 --- a/app/stable_appconfig.go +++ b/app/stable_appconfig.go @@ -14,6 +14,7 @@ import ( paramsclient "github.com/cosmos/cosmos-sdk/x/params/client" paramskeeper "github.com/cosmos/cosmos-sdk/x/params/keeper" upgradeclient "github.com/cosmos/cosmos-sdk/x/upgrade/client" + "github.com/regen-network/regen-ledger/types/module/server" ) diff --git a/go.sum b/go.sum index 0b09c52187..45ad6aadb8 100644 --- a/go.sum +++ b/go.sum @@ -840,7 +840,6 @@ golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= @@ -884,7 +883,6 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= @@ -894,7 +892,6 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U= gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= @@ -903,7 +900,6 @@ gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= @@ -913,7 +909,6 @@ gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= diff --git a/types/module/server/manager.go b/types/module/server/manager.go index bd72ff50fc..d01dab3e47 100644 --- a/types/module/server/manager.go +++ b/types/module/server/manager.go @@ -18,23 +18,34 @@ import ( // Manager is the server module manager type Manager struct { - baseApp *baseapp.BaseApp - cdc *codec.ProtoCodec - keys map[string]ModuleKey - router *router - requiredServices map[reflect.Type]bool - initGenesisHandlers map[string]module.InitGenesisHandler - exportGenesisHandlers map[string]module.ExportGenesisHandler + baseApp *baseapp.BaseApp + cdc *codec.ProtoCodec + keys map[string]ModuleKey + router *router + requiredServices map[reflect.Type]bool + initGenesisHandlers map[string]module.InitGenesisHandler + exportGenesisHandlers map[string]module.ExportGenesisHandler + registerInvariantsHandler map[string]RegisterInvariantsHandler +} + +// RegisterInvariants registers all module routes and module querier routes +func (mm *Manager) RegisterInvariants(ir sdk.InvariantRegistry) { + for _, invariant := range mm.registerInvariantsHandler { + if invariant != nil { + invariant(ir) + } + } } // NewManager creates a new Manager func NewManager(baseApp *baseapp.BaseApp, cdc *codec.ProtoCodec) *Manager { return &Manager{ - baseApp: baseApp, - cdc: cdc, - keys: map[string]ModuleKey{}, - initGenesisHandlers: map[string]module.InitGenesisHandler{}, - exportGenesisHandlers: map[string]module.ExportGenesisHandler{}, + baseApp: baseApp, + cdc: cdc, + keys: map[string]ModuleKey{}, + registerInvariantsHandler: map[string]RegisterInvariantsHandler{}, + initGenesisHandlers: map[string]module.InitGenesisHandler{}, + exportGenesisHandlers: map[string]module.ExportGenesisHandler{}, router: &router{ handlers: map[string]handler{}, providedServices: map[reflect.Type]bool{}, @@ -105,6 +116,7 @@ func (mm *Manager) RegisterModules(modules []module.Module) error { } serverMod.RegisterServices(cfg) + mm.registerInvariantsHandler[name] = cfg.registerInvariantsHandler mm.initGenesisHandlers[name] = cfg.initGenesisHandler mm.exportGenesisHandlers[name] = cfg.exportGenesisHandler @@ -211,15 +223,18 @@ func exportGenesis(ctx sdk.Context, cdc codec.JSONMarshaler, exportGenesisHandle return genesisData, nil } +type RegisterInvariantsHandler func(ir sdk.InvariantRegistry) + type configurator struct { - msgServer gogogrpc.Server - queryServer gogogrpc.Server - key *rootModuleKey - cdc codec.Marshaler - requiredServices map[reflect.Type]bool - router sdk.Router - initGenesisHandler module.InitGenesisHandler - exportGenesisHandler module.ExportGenesisHandler + msgServer gogogrpc.Server + queryServer gogogrpc.Server + key *rootModuleKey + cdc codec.Marshaler + requiredServices map[reflect.Type]bool + router sdk.Router + initGenesisHandler module.InitGenesisHandler + exportGenesisHandler module.ExportGenesisHandler + registerInvariantsHandler RegisterInvariantsHandler } var _ Configurator = &configurator{} @@ -232,6 +247,10 @@ func (c *configurator) QueryServer() gogogrpc.Server { return c.queryServer } +func (c *configurator) RegisterInvariantsHandler(registry RegisterInvariantsHandler) { + c.registerInvariantsHandler = registry +} + func (c *configurator) RegisterGenesisHandlers(initGenesisHandler module.InitGenesisHandler, exportGenesisHandler module.ExportGenesisHandler) { c.initGenesisHandler = initGenesisHandler c.exportGenesisHandler = exportGenesisHandler diff --git a/types/module/server/module.go b/types/module/server/module.go index 99ca5bb1d9..ba2762c4f2 100644 --- a/types/module/server/module.go +++ b/types/module/server/module.go @@ -4,6 +4,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" sdkmodule "github.com/cosmos/cosmos-sdk/types/module" + "github.com/regen-network/regen-ledger/types/module" ) @@ -20,6 +21,7 @@ type Configurator interface { ModuleKey() RootModuleKey Marshaler() codec.Marshaler RequireServer(interface{}) + RegisterInvariantsHandler(registry RegisterInvariantsHandler) RegisterGenesisHandlers(module.InitGenesisHandler, module.ExportGenesisHandler) // Router() is temporarily added here to use in the group module. diff --git a/types/module/server/testutil.go b/types/module/server/testutil.go index fe130317f3..3d87ed2973 100644 --- a/types/module/server/testutil.go +++ b/types/module/server/testutil.go @@ -6,8 +6,6 @@ import ( "fmt" "testing" - "github.com/regen-network/regen-ledger/testutil" - "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/codec/types" @@ -20,6 +18,7 @@ import ( dbm "github.com/tendermint/tm-db" "google.golang.org/grpc" + "github.com/regen-network/regen-ledger/testutil" regentypes "github.com/regen-network/regen-ledger/types" "github.com/regen-network/regen-ledger/types/module" ) diff --git a/x/data/server/server_test.go b/x/data/server/server_test.go index 284837c2c9..7c34033517 100644 --- a/x/data/server/server_test.go +++ b/x/data/server/server_test.go @@ -3,10 +3,9 @@ package server_test import ( "testing" - "github.com/regen-network/regen-ledger/types/module" - "github.com/stretchr/testify/suite" + "github.com/regen-network/regen-ledger/types/module" "github.com/regen-network/regen-ledger/types/module/server" datamodule "github.com/regen-network/regen-ledger/x/data/module" "github.com/regen-network/regen-ledger/x/data/server/testsuite" diff --git a/x/data/server/testsuite/suite.go b/x/data/server/testsuite/suite.go index 6b60c9eb72..8f2bfad70d 100644 --- a/x/data/server/testsuite/suite.go +++ b/x/data/server/testsuite/suite.go @@ -3,11 +3,10 @@ package testsuite import ( "context" - "github.com/regen-network/regen-ledger/testutil" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/suite" + "github.com/regen-network/regen-ledger/testutil" "github.com/regen-network/regen-ledger/x/data" ) diff --git a/x/group/client/testsuite/query_test.go b/x/group/client/testsuite/query_test.go index 00af59f854..b681b6c573 100644 --- a/x/group/client/testsuite/query_test.go +++ b/x/group/client/testsuite/query_test.go @@ -6,10 +6,11 @@ import ( "fmt" "strconv" + tmcli "github.com/tendermint/tendermint/libs/cli" + "github.com/regen-network/regen-ledger/testutil/cli" "github.com/regen-network/regen-ledger/x/group" "github.com/regen-network/regen-ledger/x/group/client" - tmcli "github.com/tendermint/tendermint/libs/cli" ) func (s *IntegrationTestSuite) TestQueryGroupInfo() { diff --git a/x/group/client/testsuite/tx_test.go b/x/group/client/testsuite/tx_test.go index b1bb0713c2..53e02c39e2 100644 --- a/x/group/client/testsuite/tx_test.go +++ b/x/group/client/testsuite/tx_test.go @@ -8,22 +8,20 @@ import ( "strings" "testing" - tmcli "github.com/tendermint/tendermint/libs/cli" - "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/crypto/hd" "github.com/cosmos/cosmos-sdk/crypto/keyring" "github.com/cosmos/cosmos-sdk/testutil" + sdk "github.com/cosmos/cosmos-sdk/types" banktestutil "github.com/cosmos/cosmos-sdk/x/bank/client/testutil" "github.com/gogo/protobuf/proto" + "github.com/stretchr/testify/suite" + tmcli "github.com/tendermint/tendermint/libs/cli" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/regen-network/regen-ledger/testutil/cli" "github.com/regen-network/regen-ledger/testutil/network" - "github.com/regen-network/regen-ledger/x/group" "github.com/regen-network/regen-ledger/x/group/client" - "github.com/stretchr/testify/suite" ) type IntegrationTestSuite struct { diff --git a/x/group/server/invariants.go b/x/group/server/invariants.go new file mode 100644 index 0000000000..53c3019f64 --- /dev/null +++ b/x/group/server/invariants.go @@ -0,0 +1,171 @@ +package server + +import ( + "math" + + "github.com/cockroachdb/apd/v2" + sdk "github.com/cosmos/cosmos-sdk/types" + + regenMath "github.com/regen-network/regen-ledger/math" + "github.com/regen-network/regen-ledger/orm" + "github.com/regen-network/regen-ledger/x/group" +) + +const ( + votesInvariant = "Tally-Votes" + weightInvariant = "Group-TotalWeight" +) + +func (s serverImpl) RegisterInvariants(ir sdk.InvariantRegistry) { + ir.RegisterRoute(group.ModuleName, votesInvariant, s.tallyVotesInvariant()) + ir.RegisterRoute(group.ModuleName, weightInvariant, s.groupTotalWeightInvariant()) +} + +func (s serverImpl) tallyVotesInvariant() sdk.Invariant { + return func(ctx sdk.Context) (string, bool) { + if ctx.BlockHeight()-1 < 0 { + return sdk.FormatInvariant(group.ModuleName, votesInvariant, "Not enough blocks to perform TallyVotesInvariant"), false + } + prevCtx, _ := ctx.CacheContext() + prevCtx = prevCtx.WithBlockHeight(ctx.BlockHeight() - 1) + msg, broken, err := tallyVotesInvariant(ctx, prevCtx, s.proposalTable) + if err != nil { + panic(err) + } + return sdk.FormatInvariant(group.ModuleName, votesInvariant, msg), broken + } +} + +func (s serverImpl) groupTotalWeightInvariant() sdk.Invariant { + return func(ctx sdk.Context) (string, bool) { + msg, broken, err := groupTotalWeightInvariant(ctx, s.groupTable, s.groupMemberByGroupIndex) + if err != nil { + panic(err) + } + return sdk.FormatInvariant(group.ModuleName, weightInvariant, msg), broken + } +} + +func tallyVotesInvariant(ctx sdk.Context, prevCtx sdk.Context, proposalTable orm.AutoUInt64Table) (string, bool, error) { + + var msg string + var broken bool + + prevIt, err := proposalTable.PrefixScan(prevCtx, 1, math.MaxUint64) + if err != nil { + return msg, broken, err + } + curIt, err := proposalTable.PrefixScan(ctx, 1, math.MaxUint64) + if err != nil { + return msg, broken, err + } + + var curProposals []*group.Proposal + _, err = orm.ReadAll(curIt, &curProposals) + if err != nil { + return msg, broken, err + } + + var prevProposals []*group.Proposal + _, err = orm.ReadAll(prevIt, &prevProposals) + if err != nil { + return msg, broken, err + } + + for i := 0; i < len(prevProposals); i++ { + if prevProposals[i].ProposalId == curProposals[i].ProposalId { + prevYesCount, err := prevProposals[i].VoteState.GetYesCount() + if err != nil { + return msg, broken, err + } + curYesCount, err := curProposals[i].VoteState.GetYesCount() + if err != nil { + return msg, broken, err + } + prevNoCount, err := prevProposals[i].VoteState.GetNoCount() + if err != nil { + return msg, broken, err + } + curNoCount, err := curProposals[i].VoteState.GetNoCount() + if err != nil { + return msg, broken, err + } + prevAbstainCount, err := prevProposals[i].VoteState.GetAbstainCount() + if err != nil { + return msg, broken, err + } + curAbstainCount, err := curProposals[i].VoteState.GetAbstainCount() + if err != nil { + return msg, broken, err + } + prevVetoCount, err := prevProposals[i].VoteState.GetVetoCount() + if err != nil { + return msg, broken, err + } + curVetoCount, err := curProposals[i].VoteState.GetVetoCount() + if err != nil { + return msg, broken, err + } + if (curYesCount.Cmp(prevYesCount) == -1) || (curNoCount.Cmp(prevNoCount) == -1) || (curAbstainCount.Cmp(prevAbstainCount) == -1) || (curVetoCount.Cmp(prevVetoCount) == -1) { + broken = true + msg += "vote tally sums must never have less than the block before\n" + return msg, broken, err + } + } + } + return msg, broken, err +} + +func groupTotalWeightInvariant(ctx sdk.Context, groupTable orm.Table, groupMemberByGroupIndex orm.UInt64Index) (string, bool, error) { + + var msg string + var broken bool + + var groupInfo group.GroupInfo + var groupMember group.GroupMember + + groupIt, err := groupTable.PrefixScan(ctx, nil, nil) + if err != nil { + return msg, broken, err + } + defer groupIt.Close() + + membersWeight := apd.New(0, 0) + + for { + _, err := groupIt.LoadNext(&groupInfo) + if orm.ErrIteratorDone.Is(err) { + break + } + memIt, err := groupMemberByGroupIndex.Get(ctx, groupInfo.GroupId) + if err != nil { + return msg, broken, err + } + defer memIt.Close() + + for { + _, err = memIt.LoadNext(&groupMember) + if orm.ErrIteratorDone.Is(err) { + break + } + curMemWeight, err := regenMath.ParseNonNegativeDecimal(groupMember.GetMember().GetWeight()) + if err != nil { + return msg, broken, err + } + err = regenMath.Add(membersWeight, membersWeight, curMemWeight) + if err != nil { + return msg, broken, err + } + } + groupWeight, err := regenMath.ParseNonNegativeDecimal(groupInfo.GetTotalWeight()) + if err != nil { + return msg, broken, err + } + if groupWeight.Cmp(membersWeight) != 0 { + broken = true + msg += "group's TotalWeight must be equal to the sum of its members' weights\n" + break + } + } + return msg, broken, err +} diff --git a/x/group/server/invariants_test.go b/x/group/server/invariants_test.go new file mode 100644 index 0000000000..bd573a8188 --- /dev/null +++ b/x/group/server/invariants_test.go @@ -0,0 +1,341 @@ +package server + +import ( + "testing" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/store" + "github.com/cosmos/cosmos-sdk/testutil/testdata" + sdk "github.com/cosmos/cosmos-sdk/types" + gogotypes "github.com/gogo/protobuf/types" + "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/libs/log" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + dbm "github.com/tendermint/tm-db" + + "github.com/regen-network/regen-ledger/orm" + "github.com/regen-network/regen-ledger/x/group" +) + +func getCtxCodecKey(t *testing.T) (sdk.Context, *codec.ProtoCodec, *sdk.KVStoreKey) { + interfaceRegistry := types.NewInterfaceRegistry() + cdc := codec.NewProtoCodec(interfaceRegistry) + key := sdk.NewKVStoreKey(group.ModuleName) + db := dbm.NewMemDB() + cms := store.NewCommitMultiStore(db) + cms.MountStoreWithDB(key, sdk.StoreTypeIAVL, db) + err := cms.LoadLatestVersion() + require.NoError(t, err) + curCtx := sdk.NewContext(cms, tmproto.Header{}, false, log.NewNopLogger()) + curCtx = curCtx.WithBlockHeight(10) + return curCtx, cdc, key +} + +func TestTallyVotesInvariant(t *testing.T) { + curCtx, cdc, key := getCtxCodecKey(t) + prevCtx, _ := curCtx.CacheContext() + prevCtx = prevCtx.WithBlockHeight(curCtx.BlockHeight() - 1) + + // Proposal Table + proposalTableBuilder := orm.NewAutoUInt64TableBuilder(ProposalTablePrefix, ProposalTableSeqPrefix, key, &group.Proposal{}, cdc) + proposalTable := proposalTableBuilder.Build() + + _, _, addr1 := testdata.KeyTestPubAddr() + _, _, addr2 := testdata.KeyTestPubAddr() + + curBlockTime, err := gogotypes.TimestampProto(curCtx.BlockTime()) + require.NoError(t, err) + prevBlockTime, err := gogotypes.TimestampProto(prevCtx.BlockTime()) + require.NoError(t, err) + + specs := map[string]struct { + prevReq []*group.Proposal + curReq []*group.Proposal + expErr bool + }{ + "invariant not broken": { + prevReq: []*group.Proposal{ + { + ProposalId: 0, + Address: addr1.String(), + Proposers: []string{addr1.String()}, + SubmittedAt: *prevBlockTime, + GroupVersion: 1, + GroupAccountVersion: 1, + Status: group.ProposalStatusSubmitted, + Result: group.ProposalResultUnfinalized, + VoteState: group.Tally{YesCount: "1", NoCount: "0", AbstainCount: "0", VetoCount: "0"}, + Timeout: gogotypes.Timestamp{Seconds: 600}, + ExecutorResult: group.ProposalExecutorResultNotRun, + }, + }, + + curReq: []*group.Proposal{ + { + ProposalId: 0, + Address: addr2.String(), + Proposers: []string{addr2.String()}, + SubmittedAt: *curBlockTime, + GroupVersion: 1, + GroupAccountVersion: 1, + Status: group.ProposalStatusSubmitted, + Result: group.ProposalResultUnfinalized, + VoteState: group.Tally{YesCount: "2", NoCount: "0", AbstainCount: "0", VetoCount: "0"}, + Timeout: gogotypes.Timestamp{Seconds: 600}, + ExecutorResult: group.ProposalExecutorResultNotRun, + }, + }, + }, + "current block yes vote count must be greater than previous block yes vote count": { + prevReq: []*group.Proposal{ + { + ProposalId: 0, + Address: addr1.String(), + Proposers: []string{addr1.String()}, + SubmittedAt: *prevBlockTime, + GroupVersion: 1, + GroupAccountVersion: 1, + Status: group.ProposalStatusSubmitted, + Result: group.ProposalResultUnfinalized, + VoteState: group.Tally{YesCount: "2", NoCount: "0", AbstainCount: "0", VetoCount: "0"}, + Timeout: gogotypes.Timestamp{Seconds: 600}, + ExecutorResult: group.ProposalExecutorResultNotRun, + }, + }, + curReq: []*group.Proposal{ + { + ProposalId: 0, + Address: addr2.String(), + Proposers: []string{addr2.String()}, + SubmittedAt: *curBlockTime, + GroupVersion: 1, + GroupAccountVersion: 1, + Status: group.ProposalStatusSubmitted, + Result: group.ProposalResultUnfinalized, + VoteState: group.Tally{YesCount: "1", NoCount: "0", AbstainCount: "0", VetoCount: "0"}, + Timeout: gogotypes.Timestamp{Seconds: 600}, + ExecutorResult: group.ProposalExecutorResultNotRun, + }, + }, + expErr: true, + }, + "current block no vote count must be greater than previous block no vote count": { + prevReq: []*group.Proposal{ + { + ProposalId: 0, + Address: addr1.String(), + Proposers: []string{addr1.String()}, + SubmittedAt: *prevBlockTime, + GroupVersion: 1, + GroupAccountVersion: 1, + Status: group.ProposalStatusSubmitted, + Result: group.ProposalResultUnfinalized, + VoteState: group.Tally{YesCount: "0", NoCount: "2", AbstainCount: "0", VetoCount: "0"}, + Timeout: gogotypes.Timestamp{Seconds: 600}, + ExecutorResult: group.ProposalExecutorResultNotRun, + }, + }, + curReq: []*group.Proposal{ + { + ProposalId: 0, + Address: addr2.String(), + Proposers: []string{addr2.String()}, + SubmittedAt: *curBlockTime, + GroupVersion: 1, + GroupAccountVersion: 1, + Status: group.ProposalStatusSubmitted, + Result: group.ProposalResultUnfinalized, + VoteState: group.Tally{YesCount: "0", NoCount: "1", AbstainCount: "0", VetoCount: "0"}, + Timeout: gogotypes.Timestamp{Seconds: 600}, + ExecutorResult: group.ProposalExecutorResultNotRun, + }, + }, + expErr: true, + }, + "current block abstain vote count must be greater than previous block abstain vote count": { + prevReq: []*group.Proposal{ + { + ProposalId: 0, + Address: addr1.String(), + Proposers: []string{addr1.String()}, + SubmittedAt: *prevBlockTime, + GroupVersion: 1, + GroupAccountVersion: 1, + Status: group.ProposalStatusSubmitted, + Result: group.ProposalResultUnfinalized, + VoteState: group.Tally{YesCount: "0", NoCount: "0", AbstainCount: "2", VetoCount: "0"}, + Timeout: gogotypes.Timestamp{Seconds: 600}, + ExecutorResult: group.ProposalExecutorResultNotRun, + }, + }, + curReq: []*group.Proposal{ + { + ProposalId: 0, + Address: addr2.String(), + Proposers: []string{addr2.String()}, + SubmittedAt: *curBlockTime, + GroupVersion: 1, + GroupAccountVersion: 1, + Status: group.ProposalStatusSubmitted, + Result: group.ProposalResultUnfinalized, + VoteState: group.Tally{YesCount: "0", NoCount: "0", AbstainCount: "1", VetoCount: "0"}, + Timeout: gogotypes.Timestamp{Seconds: 600}, + ExecutorResult: group.ProposalExecutorResultNotRun, + }, + }, + expErr: true, + }, + "current block veto vote count must be greater than previous block veto vote count": { + prevReq: []*group.Proposal{ + { + ProposalId: 0, + Address: addr1.String(), + Proposers: []string{addr1.String()}, + SubmittedAt: *prevBlockTime, + GroupVersion: 1, + GroupAccountVersion: 1, + Status: group.ProposalStatusSubmitted, + Result: group.ProposalResultUnfinalized, + VoteState: group.Tally{YesCount: "0", NoCount: "0", AbstainCount: "0", VetoCount: "2"}, + Timeout: gogotypes.Timestamp{Seconds: 600}, + ExecutorResult: group.ProposalExecutorResultNotRun, + }, + }, + curReq: []*group.Proposal{ + { + ProposalId: 0, + Address: addr2.String(), + Proposers: []string{addr2.String()}, + SubmittedAt: *curBlockTime, + GroupVersion: 1, + GroupAccountVersion: 1, + Status: group.ProposalStatusSubmitted, + Result: group.ProposalResultUnfinalized, + VoteState: group.Tally{YesCount: "0", NoCount: "0", AbstainCount: "0", VetoCount: "1"}, + Timeout: gogotypes.Timestamp{Seconds: 600}, + ExecutorResult: group.ProposalExecutorResultNotRun, + }, + }, + expErr: true, + }, + } + + for _, spec := range specs { + + prevProposals := spec.prevReq + curProposals := spec.curReq + + cachePrevCtx, _ := prevCtx.CacheContext() + cacheCurCtx, _ := curCtx.CacheContext() + + for i := 0; i < len(prevProposals) && i < len(curProposals); i++ { + _, err = proposalTable.Create(cachePrevCtx, prevProposals[i]) + require.NoError(t, err) + _, err = proposalTable.Create(cacheCurCtx, curProposals[i]) + require.NoError(t, err) + } + _, broken, _ := tallyVotesInvariant(cacheCurCtx, cachePrevCtx, proposalTable) + require.Equal(t, spec.expErr, broken) + } +} + +func TestGroupTotalWeightInvariant(t *testing.T) { + curCtx, cdc, key := getCtxCodecKey(t) + + // Group Table + groupTableBuilder := orm.NewTableBuilder(GroupTablePrefix, key, &group.GroupInfo{}, orm.FixLengthIndexKeys(orm.EncodedSeqLength), cdc) + groupTable := groupTableBuilder.Build() + + // Group Member Table + groupMemberTableBuilder := orm.NewPrimaryKeyTableBuilder(GroupMemberTablePrefix, key, &group.GroupMember{}, orm.Max255DynamicLengthIndexKeyCodec{}, cdc) + groupMemberByGroupIndex := orm.NewUInt64Index(groupMemberTableBuilder, GroupMemberByGroupIndexPrefix, func(val interface{}) ([]uint64, error) { + group := val.(*group.GroupMember).GroupId + return []uint64{group}, nil + }) + groupMemberTable := groupMemberTableBuilder.Build() + + _, _, addr1 := testdata.KeyTestPubAddr() + _, _, addr2 := testdata.KeyTestPubAddr() + + specs := map[string]struct { + groupReq []*group.GroupInfo + membersReq []*group.GroupMember + expErr bool + }{ + "invariant not broken": { + groupReq: []*group.GroupInfo{ + { + GroupId: 1, + Admin: addr1.String(), + Version: 1, + TotalWeight: "3", + }, + }, + membersReq: []*group.GroupMember{ + { + GroupId: 1, + Member: &group.Member{ + Address: addr1.String(), + Weight: "1", + }, + }, + { + GroupId: 1, + Member: &group.Member{ + Address: addr2.String(), + Weight: "2", + }, + }, + }, + expErr: false, + }, + + "group's TotalWeight must be equal to sum of its members weight ": { + groupReq: []*group.GroupInfo{ + { + GroupId: 1, + Admin: addr1.String(), + Version: 1, + TotalWeight: "3", + }, + }, + membersReq: []*group.GroupMember{ + { + GroupId: 1, + Member: &group.Member{ + Address: addr1.String(), + Weight: "2", + }, + }, + { + GroupId: 1, + Member: &group.Member{ + Address: addr2.String(), + Weight: "2", + }, + }, + }, + expErr: true, + }, + } + + for _, spec := range specs { + cacheCurCtx, _ := curCtx.CacheContext() + groupReq := spec.groupReq + members := spec.membersReq + + for i := 0; i < len(groupReq); i++ { + err := groupTable.Create(cacheCurCtx, group.ID(groupReq[i].GroupId).Bytes(), groupReq[i]) + require.NoError(t, err) + } + + for i := 0; i < len(members); i++ { + err := groupMemberTable.Create(cacheCurCtx, members[i]) + require.NoError(t, err) + } + + _, broken, _ := groupTotalWeightInvariant(cacheCurCtx, groupTable, groupMemberByGroupIndex) + require.Equal(t, spec.expErr, broken) + } +} diff --git a/x/group/server/server.go b/x/group/server/server.go index 326e7866fa..6f032a52bf 100644 --- a/x/group/server/server.go +++ b/x/group/server/server.go @@ -1,12 +1,12 @@ package server import ( + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/regen-network/regen-ledger/orm" servermodule "github.com/regen-network/regen-ledger/types/module/server" "github.com/regen-network/regen-ledger/x/group" - - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" ) const ( @@ -164,5 +164,6 @@ func RegisterServices(configurator servermodule.Configurator, accountKeeper Acco impl := newServer(configurator.ModuleKey(), configurator.Router(), accountKeeper, configurator.Marshaler()) group.RegisterMsgServer(configurator.MsgServer(), impl) group.RegisterQueryServer(configurator.QueryServer(), impl) + configurator.RegisterInvariantsHandler(impl.RegisterInvariants) configurator.RegisterGenesisHandlers(impl.InitGenesis, impl.ExportGenesis) } diff --git a/x/group/server/server_test.go b/x/group/server/server_test.go index 9c73037c14..54720e7400 100644 --- a/x/group/server/server_test.go +++ b/x/group/server/server_test.go @@ -11,7 +11,6 @@ import ( bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" paramstypes "github.com/cosmos/cosmos-sdk/x/params/types" - "github.com/stretchr/testify/suite" "github.com/regen-network/regen-ledger/types/module" diff --git a/x/group/server/testsuite/suite.go b/x/group/server/testsuite/suite.go index 98b301546f..fd1570ba7f 100644 --- a/x/group/server/testsuite/suite.go +++ b/x/group/server/testsuite/suite.go @@ -7,23 +7,28 @@ import ( "strings" "time" + "github.com/regen-network/regen-ledger/testutil/testdata" + "github.com/regen-network/regen-ledger/testutil" servermodule "github.com/regen-network/regen-ledger/types/module/server" authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" + bank "github.com/cosmos/cosmos-sdk/x/bank" + gogotypes "github.com/gogo/protobuf/types" + "github.com/stretchr/testify/suite" "github.com/cosmos/cosmos-sdk/baseapp" sdk "github.com/cosmos/cosmos-sdk/types" - bank "github.com/cosmos/cosmos-sdk/x/bank" bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" - "github.com/regen-network/regen-ledger/testutil/testdata" "github.com/regen-network/regen-ledger/types" + "github.com/regen-network/regen-ledger/x/group" + groupserver "github.com/regen-network/regen-ledger/x/group/server" )