From 29575850b3a2ca313b3b7411b961a97d9f1f9916 Mon Sep 17 00:00:00 2001 From: Ru Horlick Date: Wed, 9 Jun 2021 12:13:40 +0100 Subject: [PATCH 1/4] feat: Add fixed fee for creating a new credit class (#351) --- .gitignore | 3 + Makefile | 3 +- app/app.go | 2 + app/experimental_appconfig.go | 9 +- docs/modules/ecocredit/protobuf.md | 33 ++ go.mod | 2 +- go.sum | 6 +- proto/regen/ecocredit/v1alpha1/types.proto | 15 + .../base/query/v1beta1/pagination.proto | 3 + .../proto/cosmos/base/v1beta1/coin.proto | 40 ++ x/ecocredit/expected_keepers.go | 10 + x/ecocredit/go.mod | 1 + x/ecocredit/keys.go | 8 + x/ecocredit/module/module.go | 25 +- x/ecocredit/params.go | 48 ++ x/ecocredit/server/fee.go | 30 ++ x/ecocredit/server/genesis.go | 32 ++ x/ecocredit/server/msg_server.go | 18 +- x/ecocredit/server/server.go | 17 +- x/ecocredit/server/server_test.go | 51 ++- x/ecocredit/server/testsuite/suite.go | 52 ++- x/ecocredit/types.go | 10 +- x/ecocredit/types.pb.go | 421 +++++++++++++++++- 23 files changed, 790 insertions(+), 49 deletions(-) create mode 100644 third_party/proto/cosmos/base/v1beta1/coin.proto create mode 100644 x/ecocredit/expected_keepers.go create mode 100644 x/ecocredit/keys.go create mode 100644 x/ecocredit/params.go create mode 100644 x/ecocredit/server/fee.go create mode 100644 x/ecocredit/server/genesis.go diff --git a/.gitignore b/.gitignore index 5dcefd8119..523434874d 100644 --- a/.gitignore +++ b/.gitignore @@ -190,3 +190,6 @@ build/ /proto-tools-stamp /tools-stamp dist/ + +# pre-commit +.pre-commit-config.yaml diff --git a/Makefile b/Makefile index b7c0fbb298..6bffc09f9e 100644 --- a/Makefile +++ b/Makefile @@ -272,7 +272,7 @@ endif .PHONY: run-tests test test-all $(TEST_TARGETS) test-cover: - @export VERSION=$(VERSION); + @export VERSION=$(VERSION); @bash scripts/test_cover.sh .PHONY: test-cover @@ -378,6 +378,7 @@ proto-update-deps: @mkdir -p $(COSMOS_PROTO_TYPES)/base/query/v1beta1/ @curl -sSL $(COSMOS_PROTO_URL)/base/query/v1beta1/pagination.proto > $(COSMOS_PROTO_TYPES)/base/query/v1beta1/pagination.proto + @curl -sSL $(COSMOS_PROTO_URL)/base/v1beta1/coin.proto > $(COSMOS_PROTO_TYPES)/base/v1beta1/coin.proto ############################################################################### diff --git a/app/app.go b/app/app.go index 75a010d6ca..68f3192a2b 100644 --- a/app/app.go +++ b/app/app.go @@ -90,6 +90,7 @@ import ( dbm "github.com/tendermint/tm-db" "github.com/regen-network/regen-ledger/types/module/server" + "github.com/regen-network/regen-ledger/x/ecocredit" ) const ( @@ -137,6 +138,7 @@ var ( stakingtypes.NotBondedPoolName: {authtypes.Burner, authtypes.Staking}, govtypes.ModuleName: {authtypes.Burner}, ibctransfertypes.ModuleName: {authtypes.Minter, authtypes.Burner}, + ecocredit.ModuleName: {authtypes.Burner}, } ) diff --git a/app/experimental_appconfig.go b/app/experimental_appconfig.go index 3271eb5a28..416cf55bf3 100644 --- a/app/experimental_appconfig.go +++ b/app/experimental_appconfig.go @@ -21,6 +21,7 @@ import ( 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" + ecocredittypes "github.com/regen-network/regen-ledger/x/ecocredit" ecocredit "github.com/regen-network/regen-ledger/x/ecocredit/module" group "github.com/regen-network/regen-ledger/x/group/module" ) @@ -51,10 +52,15 @@ func setCustomModules(app *RegenApp, interfaceRegistry types.InterfaceRegistry) newModuleManager := server.NewManager(app.BaseApp, codec.NewProtoCodec(interfaceRegistry)) // BEGIN HACK: this is a total, ugly hack until x/auth & x/bank supports ADR 033 or we have a suitable alternative + ecocreditModule := ecocredit.NewModule( + app.GetSubspace(ecocredittypes.DefaultParamspace), + app.BankKeeper, + ) + groupModule := group.Module{AccountKeeper: app.AccountKeeper, BankKeeper: app.BankKeeper} // use a separate newModules from the global NewModules here because we need to pass state into the group module newModules := []moduletypes.Module{ - ecocredit.Module{}, + ecocreditModule, data.Module{}, groupModule, } @@ -135,4 +141,5 @@ func (app *RegenApp) setCustomSimulationManager() []module.AppModuleSimulation { } func initCustomParamsKeeper(paramsKeeper *paramskeeper.Keeper) { + paramsKeeper.Subspace(ecocredittypes.DefaultParamspace) } diff --git a/docs/modules/ecocredit/protobuf.md b/docs/modules/ecocredit/protobuf.md index 0966213a45..435173fe73 100644 --- a/docs/modules/ecocredit/protobuf.md +++ b/docs/modules/ecocredit/protobuf.md @@ -13,6 +13,8 @@ - [regen/ecocredit/v1alpha1/types.proto](#regen/ecocredit/v1alpha1/types.proto) - [BatchInfo](#regen.ecocredit.v1alpha1.BatchInfo) - [ClassInfo](#regen.ecocredit.v1alpha1.ClassInfo) + - [GenesisState](#regen.ecocredit.v1alpha1.GenesisState) + - [Params](#regen.ecocredit.v1alpha1.Params) - [regen/ecocredit/v1alpha1/query.proto](#regen/ecocredit/v1alpha1/query.proto) - [QueryBalanceRequest](#regen.ecocredit.v1alpha1.QueryBalanceRequest) @@ -181,6 +183,37 @@ ClassInfo represents the high-level on-chain information for a credit class. + + + +### GenesisState +GenesisState defines the state of the ecocredit module that is needed at genesis + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| params | [Params](#regen.ecocredit.v1alpha1.Params) | | Params contains the updateable global parameters for use with the x/params module | + + + + + + + + +### Params +Params defines the updatable global parameters of the ecocredit module for +use with the x/params module. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| credit_class_fee | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | credit_class_fee is the fixed fee charged on creation of a new credit class | + + + + + diff --git a/go.mod b/go.mod index 748f7d3fb8..3ef9cd7002 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( github.com/tendermint/tendermint v0.34.10 github.com/tendermint/tm-db v0.6.4 golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a // indirect - google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d // indirect + google.golang.org/genproto v0.0.0-20210611144927-798beca9d670 // indirect gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect ) diff --git a/go.sum b/go.sum index 9332cd00ca..0812474876 100644 --- a/go.sum +++ b/go.sum @@ -127,7 +127,6 @@ github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7 github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/cosmos/cosmos-sdk v0.43.0-beta1 h1:cfRZY+opamo+zF+MuEbvriZwoSzfCuEh1fqUM8fFHbg= github.com/cosmos/cosmos-sdk v0.43.0-beta1/go.mod h1:rpCPaC3MnityU4Io4CDZqZB4GMtPqNeYXxPk8iRqmYM= github.com/cosmos/cosmos-sdk v0.43.0-beta1.0.20210520130629-fbb50cfa0a43 h1:50vB4gU07eqPc3LqzBOYXxXt7uiwkzsBMF/q9mms7UY= github.com/cosmos/cosmos-sdk v0.43.0-beta1.0.20210520130629-fbb50cfa0a43/go.mod h1:rpCPaC3MnityU4Io4CDZqZB4GMtPqNeYXxPk8iRqmYM= @@ -300,7 +299,6 @@ github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OI github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= @@ -989,8 +987,8 @@ google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20201111145450-ac7456db90a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201119123407-9b1e624d6bc4/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210114201628-6edceaf6022f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d h1:KzwjikDymrEmYYbdyfievTwjEeGlu+OM6oiKBkF3Jfg= -google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210611144927-798beca9d670 h1:M9c2dapWGIISuWaz5vr/RUk5Qn2Hs8DZ9CJb5aH266Q= +google.golang.org/genproto v0.0.0-20210611144927-798beca9d670/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= google.golang.org/grpc v1.33.2 h1:EQyQC3sa8M+p6Ulc8yy9SWSS2GVwyRc83gAbG8lrl4o= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= diff --git a/proto/regen/ecocredit/v1alpha1/types.proto b/proto/regen/ecocredit/v1alpha1/types.proto index aa1c4e38a5..695b667a71 100644 --- a/proto/regen/ecocredit/v1alpha1/types.proto +++ b/proto/regen/ecocredit/v1alpha1/types.proto @@ -2,6 +2,7 @@ syntax = "proto3"; package regen.ecocredit.v1alpha1; +import "cosmos/base/v1beta1/coin.proto"; import "gogoproto/gogo.proto"; option go_package = "github.com/regen-network/regen-ledger/x/ecocredit"; @@ -40,4 +41,18 @@ message BatchInfo { // metadata is any arbitrary metadata to attached to the credit batch. bytes metadata = 5; +} + +// Params defines the updatable global parameters of the ecocredit module for +// use with the x/params module. +message Params { + // credit_class_fee is the fixed fee charged on creation of a new credit class + repeated cosmos.base.v1beta1.Coin credit_class_fee = 1 + [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; +} + +// GenesisState defines the state of the ecocredit module that is needed at genesis +message GenesisState { + // Params contains the updateable global parameters for use with the x/params module + Params params = 1 [(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"params\""]; } \ No newline at end of file diff --git a/third_party/proto/cosmos/base/query/v1beta1/pagination.proto b/third_party/proto/cosmos/base/query/v1beta1/pagination.proto index 2a8cbccedd..784c479562 100644 --- a/third_party/proto/cosmos/base/query/v1beta1/pagination.proto +++ b/third_party/proto/cosmos/base/query/v1beta1/pagination.proto @@ -30,6 +30,9 @@ message PageRequest { // count_total is only respected when offset is used. It is ignored when key // is set. bool count_total = 4; + + // reverse is set to true if results are to be returned in the descending order. + bool reverse = 5; } // PageResponse is to be embedded in gRPC response messages where the diff --git a/third_party/proto/cosmos/base/v1beta1/coin.proto b/third_party/proto/cosmos/base/v1beta1/coin.proto new file mode 100644 index 0000000000..fab75284b7 --- /dev/null +++ b/third_party/proto/cosmos/base/v1beta1/coin.proto @@ -0,0 +1,40 @@ +syntax = "proto3"; +package cosmos.base.v1beta1; + +import "gogoproto/gogo.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/types"; +option (gogoproto.goproto_stringer_all) = false; +option (gogoproto.stringer_all) = false; + +// Coin defines a token with a denomination and an amount. +// +// NOTE: The amount field is an Int which implements the custom method +// signatures required by gogoproto. +message Coin { + option (gogoproto.equal) = true; + + string denom = 1; + string amount = 2 [(gogoproto.customtype) = "Int", (gogoproto.nullable) = false]; +} + +// DecCoin defines a token with a denomination and a decimal amount. +// +// NOTE: The amount field is an Dec which implements the custom method +// signatures required by gogoproto. +message DecCoin { + option (gogoproto.equal) = true; + + string denom = 1; + string amount = 2 [(gogoproto.customtype) = "Dec", (gogoproto.nullable) = false]; +} + +// IntProto defines a Protobuf wrapper around an Int object. +message IntProto { + string int = 1 [(gogoproto.customtype) = "Int", (gogoproto.nullable) = false]; +} + +// DecProto defines a Protobuf wrapper around a Dec object. +message DecProto { + string dec = 1 [(gogoproto.customtype) = "Dec", (gogoproto.nullable) = false]; +} diff --git a/x/ecocredit/expected_keepers.go b/x/ecocredit/expected_keepers.go new file mode 100644 index 0000000000..bbc8645c6a --- /dev/null +++ b/x/ecocredit/expected_keepers.go @@ -0,0 +1,10 @@ +package ecocredit + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +type BankKeeper interface { + BurnCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error + SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error +} diff --git a/x/ecocredit/go.mod b/x/ecocredit/go.mod index 7229bdd796..834ce9a01d 100644 --- a/x/ecocredit/go.mod +++ b/x/ecocredit/go.mod @@ -15,6 +15,7 @@ require ( github.com/spf13/cobra v1.1.3 github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.7.0 + github.com/tendermint/tendermint v0.34.10 // indirect google.golang.org/grpc v1.37.0 gopkg.in/yaml.v2 v2.4.0 ) diff --git a/x/ecocredit/keys.go b/x/ecocredit/keys.go new file mode 100644 index 0000000000..4b3dcfd58e --- /dev/null +++ b/x/ecocredit/keys.go @@ -0,0 +1,8 @@ +package ecocredit + +const ( + // ModuleName is the module name constant used in many places + ModuleName = "ecocredit" + + DefaultParamspace = ModuleName +) diff --git a/x/ecocredit/module/module.go b/x/ecocredit/module/module.go index ae2a419057..9d9a47b9b0 100644 --- a/x/ecocredit/module/module.go +++ b/x/ecocredit/module/module.go @@ -7,6 +7,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/codec/types" "github.com/cosmos/cosmos-sdk/types/module" + paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" "github.com/gorilla/mux" "github.com/grpc-ecosystem/grpc-gateway/runtime" "github.com/spf13/cobra" @@ -18,14 +19,28 @@ import ( "github.com/regen-network/regen-ledger/x/ecocredit/server" ) -type Module struct{} +type Module struct { + paramSpace paramtypes.Subspace + bankKeeper ecocredit.BankKeeper +} + +func NewModule(paramSpace paramtypes.Subspace, bankKeeper ecocredit.BankKeeper) Module { + if !paramSpace.HasKeyTable() { + paramSpace = paramSpace.WithKeyTable(ecocredit.ParamKeyTable()) + } + + return Module{ + paramSpace: paramSpace, + bankKeeper: bankKeeper, + } +} var _ module.AppModuleBasic = Module{} var _ servermodule.Module = Module{} var _ climodule.Module = Module{} func (a Module) Name() string { - return "ecocredit" + return ecocredit.ModuleName } func (a Module) RegisterInterfaces(registry types.InterfaceRegistry) { @@ -33,10 +48,12 @@ func (a Module) RegisterInterfaces(registry types.InterfaceRegistry) { } func (a Module) RegisterServices(configurator servermodule.Configurator) { - server.RegisterServices(configurator) + server.RegisterServices(configurator, a.paramSpace, a.bankKeeper) } -func (a Module) DefaultGenesis(codec.JSONCodec) json.RawMessage { return nil } +func (a Module) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage { + return cdc.MustMarshalJSON(ecocredit.DefaultGenesisState()) +} func (a Module) ValidateGenesis(codec.JSONCodec, sdkclient.TxEncodingConfig, json.RawMessage) error { return nil diff --git a/x/ecocredit/params.go b/x/ecocredit/params.go new file mode 100644 index 0000000000..5379c8883b --- /dev/null +++ b/x/ecocredit/params.go @@ -0,0 +1,48 @@ +package ecocredit + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" +) + +var ( + // TODO: Decide a sensible default value + DefaultCreditClassFeeTokens = sdk.NewInt(10000) + KeyCreditClassFee = []byte("CreditClassFee") +) + +func ParamKeyTable() paramtypes.KeyTable { + return paramtypes.NewKeyTable().RegisterParamSet(&Params{}) +} + +// Implements params.ParamSet +func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs { + return paramtypes.ParamSetPairs{ + paramtypes.NewParamSetPair(KeyCreditClassFee, &p.CreditClassFee, validateCreditClassFee), + } +} + +func validateCreditClassFee(i interface{}) error { + v, ok := i.(sdk.Coins) + if !ok { + return fmt.Errorf("invalid parameter type: %T", i) + } + + if err := v.Validate(); err != nil { + return err + } + + return nil +} + +func NewParams(creditClassFee sdk.Coins) Params { + return Params{ + CreditClassFee: creditClassFee, + } +} + +func DefaultParams() Params { + return NewParams(sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, DefaultCreditClassFeeTokens))) +} diff --git a/x/ecocredit/server/fee.go b/x/ecocredit/server/fee.go new file mode 100644 index 0000000000..51c7a5a91a --- /dev/null +++ b/x/ecocredit/server/fee.go @@ -0,0 +1,30 @@ +package server + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/regen-network/regen-ledger/x/ecocredit" +) + +func (s serverImpl) GetCreditClassFee(ctx sdk.Context) sdk.Coins { + var params ecocredit.Params + s.paramSpace.GetParamSet(ctx, ¶ms) + return params.CreditClassFee +} + +func (s serverImpl) ChargeCreditClassFee(ctx sdk.Context, designerAddr sdk.AccAddress) error { + creditClassFee := s.GetCreditClassFee(ctx) + + // Move the fee to the ecocredit module's account + err := s.bankKeeper.SendCoinsFromAccountToModule(ctx, designerAddr, ecocredit.ModuleName, creditClassFee) + if err != nil { + return err + } + + // Burn the coins + // TODO: Update this implementation based on the discussion at + // https://github.com/regen-network/regen-ledger/issues/351 + err = s.bankKeeper.BurnCoins(ctx, ecocredit.ModuleName, creditClassFee) + + return nil +} diff --git a/x/ecocredit/server/genesis.go b/x/ecocredit/server/genesis.go new file mode 100644 index 0000000000..13dcbe2548 --- /dev/null +++ b/x/ecocredit/server/genesis.go @@ -0,0 +1,32 @@ +package server + +import ( + "encoding/json" + + "github.com/cosmos/cosmos-sdk/codec" + abci "github.com/tendermint/tendermint/abci/types" + + "github.com/regen-network/regen-ledger/types" + "github.com/regen-network/regen-ledger/x/ecocredit" +) + + +// InitGenesis performs genesis initialization for the ecocredit module. It +// returns no validator updates. +func (s serverImpl) InitGenesis(ctx types.Context, cdc codec.JSONCodec, data json.RawMessage) ([]abci.ValidatorUpdate, error) { + var genesisState ecocredit.GenesisState + cdc.MustUnmarshalJSON(data, &genesisState) + s.paramSpace.SetParamSet(ctx.Context, &genesisState.Params) + return []abci.ValidatorUpdate{}, nil +} + +func (s serverImpl) ExportGenesis(ctx types.Context, cdc codec.JSONCodec) (json.RawMessage, error) { + // Get Params from the store and put them in the genesis state + var params ecocredit.Params + s.paramSpace.GetParamSet(ctx.Context, ¶ms) + + gs := &ecocredit.GenesisState{ + Params: params, + } + return cdc.MustMarshalJSON(gs), nil +} diff --git a/x/ecocredit/server/msg_server.go b/x/ecocredit/server/msg_server.go index 6c85cd5b1b..c335cd6c3f 100644 --- a/x/ecocredit/server/msg_server.go +++ b/x/ecocredit/server/msg_server.go @@ -11,10 +11,15 @@ import ( "github.com/regen-network/regen-ledger/orm" "github.com/regen-network/regen-ledger/types/math" - "github.com/regen-network/regen-ledger/x/ecocredit/util" "github.com/regen-network/regen-ledger/x/ecocredit" + "github.com/regen-network/regen-ledger/x/ecocredit/util" ) +// CreateClass creates a new class of ecocredit +// +// The designer is charged a fee for creating the class. This is controlled by +// the global parameter CreditClassFee, which can be updated through the +// governance process. func (s serverImpl) CreateClass(ctx types.Context, req *ecocredit.MsgCreateClassRequest) (*ecocredit.MsgCreateClassResponse, error) { classID := s.idSeq.NextVal(ctx) classIDStr := util.Uint64ToBase58Check(classID) @@ -37,6 +42,17 @@ func (s serverImpl) CreateClass(ctx types.Context, req *ecocredit.MsgCreateClass return nil, err } + // Charge the designer a fee to create the credit class + designerAddress, err := sdk.AccAddressFromBech32(req.Designer) + if err != nil { + return nil, err + } + + err = s.ChargeCreditClassFee(ctx.Context, designerAddress) + if err != nil { + return nil, err + } + return &ecocredit.MsgCreateClassResponse{ClassId: classIDStr}, nil } diff --git a/x/ecocredit/server/server.go b/x/ecocredit/server/server.go index 3971f67936..778b86e027 100644 --- a/x/ecocredit/server/server.go +++ b/x/ecocredit/server/server.go @@ -3,6 +3,7 @@ package server import ( "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" + paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" "github.com/regen-network/regen-ledger/orm" "github.com/regen-network/regen-ledger/types/module/server" @@ -23,14 +24,21 @@ const ( type serverImpl struct { storeKey sdk.StoreKey + paramSpace paramtypes.Subspace + bankKeeper ecocredit.BankKeeper + // we use a single sequence to avoid having the same string/ID identifying a class and batch denom idSeq orm.Sequence classInfoTable orm.PrimaryKeyTable batchInfoTable orm.PrimaryKeyTable } -func newServer(storeKey sdk.StoreKey, cdc codec.Codec) serverImpl { - s := serverImpl{storeKey: storeKey} +func newServer(storeKey sdk.StoreKey, paramSpace paramtypes.Subspace, bankKeeper ecocredit.BankKeeper, cdc codec.Codec) serverImpl { + s := serverImpl{ + storeKey: storeKey, + paramSpace: paramSpace, + bankKeeper: bankKeeper, + } s.idSeq = orm.NewSequence(storeKey, IDSeqPrefix) @@ -43,8 +51,9 @@ func newServer(storeKey sdk.StoreKey, cdc codec.Codec) serverImpl { return s } -func RegisterServices(configurator server.Configurator) { - impl := newServer(configurator.ModuleKey(), configurator.Marshaler()) +func RegisterServices(configurator server.Configurator, paramSpace paramtypes.Subspace, bankKeeper ecocredit.BankKeeper) { + impl := newServer(configurator.ModuleKey(), paramSpace, bankKeeper, configurator.Marshaler()) ecocredit.RegisterMsgServer(configurator.MsgServer(), impl) ecocredit.RegisterQueryServer(configurator.QueryServer(), impl) + configurator.RegisterGenesisHandlers(impl.InitGenesis, impl.ExportGenesis) } diff --git a/x/ecocredit/server/server_test.go b/x/ecocredit/server/server_test.go index 32dab6aa0e..71a818640c 100644 --- a/x/ecocredit/server/server_test.go +++ b/x/ecocredit/server/server_test.go @@ -3,17 +3,62 @@ package server_test import ( "testing" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" + paramstypes "github.com/cosmos/cosmos-sdk/x/params/types" "github.com/stretchr/testify/suite" "github.com/regen-network/regen-ledger/types/module" "github.com/regen-network/regen-ledger/types/module/server" - ecocreditmodule "github.com/regen-network/regen-ledger/x/ecocredit/module" + ecocredittypes "github.com/regen-network/regen-ledger/x/ecocredit" + ecocredit "github.com/regen-network/regen-ledger/x/ecocredit/module" "github.com/regen-network/regen-ledger/x/ecocredit/server/testsuite" ) func TestServer(t *testing.T) { ff := server.NewFixtureFactory(t, 6) - ff.SetModules([]module.Module{ecocreditmodule.Module{}}) - s := testsuite.NewIntegrationTestSuite(ff) + baseApp := ff.BaseApp() + cdc := ff.Codec() + amino := codec.NewLegacyAmino() + + authtypes.RegisterInterfaces(cdc.InterfaceRegistry()) + + authKey := sdk.NewKVStoreKey(authtypes.StoreKey) + bankKey := sdk.NewKVStoreKey(banktypes.StoreKey) + paramsKey := sdk.NewKVStoreKey(paramstypes.StoreKey) + tkey := sdk.NewTransientStoreKey(paramstypes.TStoreKey) + + // baseApp.Router().AddRoute(sdk.NewRoute(banktypes.ModuleName, bank.NewHandler(bankKeeper))) + baseApp.MountStore(authKey, sdk.StoreTypeIAVL) + baseApp.MountStore(bankKey, sdk.StoreTypeIAVL) + baseApp.MountStore(paramsKey, sdk.StoreTypeIAVL) + baseApp.MountStore(tkey, sdk.StoreTypeTransient) + + authSubspace := paramstypes.NewSubspace(cdc, amino, paramsKey, tkey, authtypes.ModuleName) + bankSubspace := paramstypes.NewSubspace(cdc, amino, paramsKey, tkey, banktypes.ModuleName) + ecocreditSubspace := paramstypes.NewSubspace(cdc, amino, paramsKey, tkey, ecocredittypes.DefaultParamspace) + + maccPerms := map[string][]string{ + minttypes.ModuleName: {authtypes.Minter}, + ecocredittypes.ModuleName: {authtypes.Burner}, + } + + accountKeeper := authkeeper.NewAccountKeeper( + cdc, authKey, authSubspace, authtypes.ProtoBaseAccount, maccPerms, + ) + + bankKeeper := bankkeeper.NewBaseKeeper( + cdc, bankKey, accountKeeper, bankSubspace, nil, + ) + + ecocreditModule := ecocredit.NewModule(ecocreditSubspace, bankKeeper) + ff.SetModules([]module.Module{ecocreditModule}) + + s := testsuite.NewIntegrationTestSuite(ff, ecocreditSubspace, bankKeeper) suite.Run(t, s) } diff --git a/x/ecocredit/server/testsuite/suite.go b/x/ecocredit/server/testsuite/suite.go index 4bc572b3e2..ae415ae523 100644 --- a/x/ecocredit/server/testsuite/suite.go +++ b/x/ecocredit/server/testsuite/suite.go @@ -6,8 +6,12 @@ import ( "github.com/regen-network/regen-ledger/types/testutil" sdk "github.com/cosmos/cosmos-sdk/types" + bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" + minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" + paramstypes "github.com/cosmos/cosmos-sdk/x/params/types" "github.com/stretchr/testify/suite" + "github.com/regen-network/regen-ledger/types" "github.com/regen-network/regen-ledger/x/ecocredit" ) @@ -17,36 +21,69 @@ type IntegrationTestSuite struct { fixtureFactory testutil.FixtureFactory fixture testutil.Fixture + sdkCtx sdk.Context ctx context.Context msgClient ecocredit.MsgClient queryClient ecocredit.QueryClient signers []sdk.AccAddress + + paramSpace paramstypes.Subspace + bankKeeper bankkeeper.Keeper } -func NewIntegrationTestSuite(fixtureFactory testutil.FixtureFactory) *IntegrationTestSuite { - return &IntegrationTestSuite{fixtureFactory: fixtureFactory} +func NewIntegrationTestSuite(fixtureFactory testutil.FixtureFactory, paramSpace paramstypes.Subspace, bankKeeper bankkeeper.BaseKeeper) *IntegrationTestSuite { + return &IntegrationTestSuite{ + fixtureFactory: fixtureFactory, + paramSpace: paramSpace, + bankKeeper: bankKeeper, + } } func (s *IntegrationTestSuite) SetupSuite() { s.fixture = s.fixtureFactory.Setup() - s.ctx = s.fixture.Context() + + // TODO clean up once types.Context merged upstream into sdk.Context + s.sdkCtx, _ = s.fixture.Context().(types.Context).CacheContext() + s.ctx = types.Context{Context: s.sdkCtx} + + ecocreditParams := ecocredit.DefaultParams() + s.paramSpace.SetParamSet(s.sdkCtx, &ecocreditParams) + s.signers = s.fixture.Signers() s.Require().GreaterOrEqual(len(s.signers), 6) s.msgClient = ecocredit.NewMsgClient(s.fixture.TxConn()) s.queryClient = ecocredit.NewQueryClient(s.fixture.QueryConn()) } +func fundAccount(bankKeeper bankkeeper.Keeper, ctx sdk.Context, addr sdk.AccAddress, amounts sdk.Coins) error { + if err := bankKeeper.MintCoins(ctx, minttypes.ModuleName, amounts); err != nil { + return err + } + return bankKeeper.SendCoinsFromModuleToAccount(ctx, minttypes.ModuleName, addr, amounts) +} + func (s *IntegrationTestSuite) TestScenario() { - designer := s.signers[0].String() + designer := s.signers[0] issuer1 := s.signers[1].String() issuer2 := s.signers[2].String() addr1 := s.signers[3].String() addr2 := s.signers[4].String() addr3 := s.signers[5].String() - // create class + // create class with insufficient funds and it should fail createClsRes, err := s.msgClient.CreateClass(s.ctx, &ecocredit.MsgCreateClassRequest{ - Designer: designer, + Designer: designer.String(), + Issuers: []string{issuer1, issuer2}, + Metadata: nil, + }) + s.Require().Error(err) + s.Require().Nil(createClsRes) + + // create class with sufficient funds and it should succeed + s.Require().NoError(fundAccount(s.bankKeeper, s.sdkCtx, designer, sdk.NewCoins(sdk.NewInt64Coin("stake", 10000)))) + + createClsRes, err = s.msgClient.CreateClass(s.ctx, &ecocredit.MsgCreateClassRequest{ + Designer: designer.String(), Issuers: []string{issuer1, issuer2}, Metadata: nil, }) @@ -56,6 +93,9 @@ func (s *IntegrationTestSuite) TestScenario() { clsID := createClsRes.ClassId s.Require().NotEmpty(clsID) + // designer should have no funds remaining + s.Require().Equal(s.bankKeeper.GetBalance(s.sdkCtx, designer, "stake"), sdk.NewInt64Coin("stake", 0)) + // create batch t0, t1 := "10.37", "1007.3869" tSupply0 := "1017.7569" diff --git a/x/ecocredit/types.go b/x/ecocredit/types.go index 79bbe5a0da..2ebe9921c3 100644 --- a/x/ecocredit/types.go +++ b/x/ecocredit/types.go @@ -1,6 +1,8 @@ package ecocredit -import "github.com/regen-network/regen-ledger/orm" +import ( + "github.com/regen-network/regen-ledger/orm" +) var _, _ orm.PrimaryKeyed = &ClassInfo{}, &BatchInfo{} @@ -11,3 +13,9 @@ func (m *ClassInfo) PrimaryKey() []byte { func (m *BatchInfo) PrimaryKey() []byte { return []byte(m.BatchDenom) } + +func DefaultGenesisState() *GenesisState { + return &GenesisState{ + Params: DefaultParams(), + } +} diff --git a/x/ecocredit/types.pb.go b/x/ecocredit/types.pb.go index 8ed9de135a..231b7adf60 100644 --- a/x/ecocredit/types.pb.go +++ b/x/ecocredit/types.pb.go @@ -5,6 +5,8 @@ package ecocredit import ( fmt "fmt" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" + types "github.com/cosmos/cosmos-sdk/types" _ "github.com/gogo/protobuf/gogoproto" proto "github.com/gogo/protobuf/proto" io "io" @@ -179,9 +181,104 @@ func (m *BatchInfo) GetMetadata() []byte { return nil } +// Params defines the updatable global parameters of the ecocredit module for +// use with the x/params module. +type Params struct { + // credit_class_fee is the fixed fee charged on creation of a new credit class + CreditClassFee github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,1,rep,name=credit_class_fee,json=creditClassFee,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"credit_class_fee"` +} + +func (m *Params) Reset() { *m = Params{} } +func (m *Params) String() string { return proto.CompactTextString(m) } +func (*Params) ProtoMessage() {} +func (*Params) Descriptor() ([]byte, []int) { + return fileDescriptor_5342f4dcaeff1a84, []int{2} +} +func (m *Params) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Params) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Params.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Params) XXX_Merge(src proto.Message) { + xxx_messageInfo_Params.Merge(m, src) +} +func (m *Params) XXX_Size() int { + return m.Size() +} +func (m *Params) XXX_DiscardUnknown() { + xxx_messageInfo_Params.DiscardUnknown(m) +} + +var xxx_messageInfo_Params proto.InternalMessageInfo + +func (m *Params) GetCreditClassFee() github_com_cosmos_cosmos_sdk_types.Coins { + if m != nil { + return m.CreditClassFee + } + return nil +} + +// GenesisState defines the state of the ecocredit module that is needed at genesis +type GenesisState struct { + // Params contains the updateable global parameters for use with the x/params module + Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params" yaml:"params"` +} + +func (m *GenesisState) Reset() { *m = GenesisState{} } +func (m *GenesisState) String() string { return proto.CompactTextString(m) } +func (*GenesisState) ProtoMessage() {} +func (*GenesisState) Descriptor() ([]byte, []int) { + return fileDescriptor_5342f4dcaeff1a84, []int{3} +} +func (m *GenesisState) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GenesisState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GenesisState.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GenesisState) XXX_Merge(src proto.Message) { + xxx_messageInfo_GenesisState.Merge(m, src) +} +func (m *GenesisState) XXX_Size() int { + return m.Size() +} +func (m *GenesisState) XXX_DiscardUnknown() { + xxx_messageInfo_GenesisState.DiscardUnknown(m) +} + +var xxx_messageInfo_GenesisState proto.InternalMessageInfo + +func (m *GenesisState) GetParams() Params { + if m != nil { + return m.Params + } + return Params{} +} + func init() { proto.RegisterType((*ClassInfo)(nil), "regen.ecocredit.v1alpha1.ClassInfo") proto.RegisterType((*BatchInfo)(nil), "regen.ecocredit.v1alpha1.BatchInfo") + proto.RegisterType((*Params)(nil), "regen.ecocredit.v1alpha1.Params") + proto.RegisterType((*GenesisState)(nil), "regen.ecocredit.v1alpha1.GenesisState") } func init() { @@ -189,29 +286,38 @@ func init() { } var fileDescriptor_5342f4dcaeff1a84 = []byte{ - // 342 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x92, 0x31, 0x4f, 0xc2, 0x40, - 0x14, 0xc7, 0x39, 0x51, 0xa0, 0xa7, 0x89, 0x49, 0x35, 0xe4, 0xc2, 0x50, 0x48, 0xe3, 0xc0, 0x62, - 0x1b, 0xe2, 0x60, 0xe2, 0x58, 0x5d, 0x88, 0x5b, 0x13, 0x17, 0x17, 0x72, 0xf4, 0x9e, 0xa5, 0xb1, - 0xbd, 0x23, 0x77, 0x87, 0xca, 0x87, 0x30, 0xf1, 0x63, 0x39, 0x32, 0x3a, 0x11, 0x85, 0x6f, 0xc0, - 0x27, 0x30, 0x77, 0x05, 0x84, 0xd5, 0xed, 0x7e, 0x79, 0xbf, 0xf7, 0xf2, 0x7f, 0x79, 0x87, 0x2f, - 0x24, 0xa4, 0xc0, 0x43, 0x48, 0x44, 0x22, 0x81, 0x65, 0x3a, 0x7c, 0xe9, 0xd1, 0x7c, 0x3c, 0xa2, - 0xbd, 0x50, 0x4f, 0xc7, 0xa0, 0x82, 0xb1, 0x14, 0x5a, 0xb8, 0xc4, 0x5a, 0xc1, 0xd6, 0x0a, 0x36, - 0x56, 0xeb, 0x3c, 0x15, 0xa9, 0xb0, 0x52, 0x68, 0x5e, 0xa5, 0xef, 0xbf, 0x23, 0xec, 0xdc, 0xe6, - 0x54, 0xa9, 0x3e, 0x7f, 0x12, 0x6e, 0x80, 0x1b, 0x89, 0x81, 0x41, 0xc6, 0x08, 0xea, 0xa0, 0xae, - 0x13, 0x9d, 0xad, 0xe6, 0xed, 0xd3, 0x29, 0x2d, 0xf2, 0x1b, 0x7f, 0x53, 0xf1, 0xe3, 0xba, 0x7d, - 0xf6, 0x99, 0xdb, 0xc2, 0x0d, 0x06, 0x2a, 0x4b, 0x39, 0x48, 0x72, 0x60, 0xfc, 0x78, 0xcb, 0x2e, - 0xc1, 0xf5, 0x4c, 0xa9, 0x09, 0x48, 0x45, 0xaa, 0x9d, 0x6a, 0xd7, 0x89, 0x37, 0x68, 0xba, 0x0a, - 0xd0, 0x94, 0x51, 0x4d, 0xc9, 0x61, 0x07, 0x75, 0x4f, 0xe2, 0x2d, 0xfb, 0x3f, 0x08, 0x3b, 0x11, - 0xd5, 0xc9, 0xe8, 0x5f, 0x79, 0xae, 0xf1, 0xf1, 0xd0, 0x34, 0x0f, 0x18, 0x70, 0x51, 0x94, 0x91, - 0xa2, 0xe6, 0x6a, 0xde, 0x76, 0xcb, 0x96, 0x9d, 0xa2, 0x1f, 0x63, 0x4b, 0x77, 0x06, 0xdc, 0x26, - 0xae, 0x95, 0xe9, 0x48, 0xd5, 0xae, 0xb1, 0x26, 0x33, 0x50, 0x0b, 0x4d, 0xf3, 0xc1, 0x84, 0x67, - 0x5a, 0xd9, 0xb4, 0x7b, 0x03, 0x77, 0x8a, 0x7e, 0x8c, 0x2d, 0x3d, 0x18, 0xd8, 0xdb, 0xf1, 0x68, - 0x7f, 0xc7, 0xe8, 0xfe, 0x73, 0xe1, 0xa1, 0xd9, 0xc2, 0x43, 0xdf, 0x0b, 0x0f, 0x7d, 0x2c, 0xbd, - 0xca, 0x6c, 0xe9, 0x55, 0xbe, 0x96, 0x5e, 0xe5, 0xb1, 0x97, 0x66, 0x7a, 0x34, 0x19, 0x06, 0x89, - 0x28, 0x42, 0x7b, 0xc8, 0x4b, 0x0e, 0xfa, 0x55, 0xc8, 0xe7, 0x35, 0xe5, 0xc0, 0x52, 0x90, 0xe1, - 0xdb, 0xdf, 0x2f, 0x18, 0xd6, 0xec, 0x1d, 0xaf, 0x7e, 0x03, 0x00, 0x00, 0xff, 0xff, 0xfd, 0x04, - 0x0c, 0x34, 0x1f, 0x02, 0x00, 0x00, + // 485 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x53, 0xbd, 0x8e, 0xd3, 0x40, + 0x10, 0x8e, 0x09, 0xe4, 0x2e, 0x9b, 0xe3, 0x47, 0x06, 0x4e, 0x26, 0x85, 0x6d, 0x59, 0x14, 0x6e, + 0x6e, 0x8d, 0x8f, 0x02, 0x89, 0xd2, 0x87, 0x40, 0x27, 0x0a, 0x90, 0x11, 0x0d, 0x8d, 0xb5, 0xb6, + 0xe7, 0x9c, 0xd5, 0xd9, 0xbb, 0x91, 0x77, 0x73, 0x70, 0x15, 0x4f, 0x80, 0xc4, 0x73, 0xf0, 0x24, + 0x57, 0x5e, 0x49, 0x15, 0x20, 0x79, 0x83, 0x3c, 0x01, 0xda, 0x5d, 0x27, 0x24, 0x48, 0x34, 0x54, + 0x9e, 0xcf, 0x33, 0xdf, 0xec, 0x37, 0xfb, 0xcd, 0xa2, 0xc7, 0x2d, 0x54, 0xc0, 0x22, 0x28, 0x78, + 0xd1, 0x42, 0x49, 0x65, 0x74, 0x11, 0x93, 0x7a, 0x3a, 0x21, 0x71, 0x24, 0x2f, 0xa7, 0x20, 0xf0, + 0xb4, 0xe5, 0x92, 0xdb, 0x8e, 0xae, 0xc2, 0x9b, 0x2a, 0xbc, 0xae, 0x1a, 0xbb, 0x05, 0x17, 0x0d, + 0x17, 0x51, 0x4e, 0x04, 0x44, 0x17, 0x71, 0x0e, 0x92, 0xc4, 0x51, 0xc1, 0x29, 0x33, 0xcc, 0xf1, + 0x83, 0x8a, 0x57, 0x5c, 0x87, 0x91, 0x8a, 0xcc, 0xdf, 0xe0, 0x8b, 0x85, 0x86, 0x27, 0x35, 0x11, + 0xe2, 0x94, 0x9d, 0x71, 0x1b, 0xa3, 0xfd, 0x42, 0x81, 0x8c, 0x96, 0x8e, 0xe5, 0x5b, 0xe1, 0x30, + 0xb9, 0xbf, 0x9a, 0x7b, 0x77, 0x2f, 0x49, 0x53, 0x3f, 0x0f, 0xd6, 0x99, 0x20, 0xdd, 0xd3, 0xe1, + 0x69, 0x69, 0x8f, 0xd1, 0x7e, 0x09, 0x82, 0x56, 0x0c, 0x5a, 0xe7, 0x86, 0xaa, 0x4f, 0x37, 0xd8, + 0x76, 0xd0, 0x1e, 0x15, 0x62, 0x06, 0xad, 0x70, 0xfa, 0x7e, 0x3f, 0x1c, 0xa6, 0x6b, 0xa8, 0x58, + 0x0d, 0x48, 0x52, 0x12, 0x49, 0x9c, 0x9b, 0xbe, 0x15, 0x1e, 0xa4, 0x1b, 0x1c, 0xfc, 0xb2, 0xd0, + 0x30, 0x21, 0xb2, 0x98, 0xfc, 0x97, 0x9e, 0x67, 0x68, 0x94, 0x2b, 0x72, 0x56, 0x02, 0xe3, 0x8d, + 0x91, 0x94, 0x1c, 0xae, 0xe6, 0x9e, 0x6d, 0x28, 0x5b, 0xc9, 0x20, 0x45, 0x1a, 0xbd, 0x50, 0xc0, + 0x3e, 0x44, 0x03, 0xa3, 0xce, 0xe9, 0xeb, 0x31, 0x3a, 0xa4, 0x1a, 0x4a, 0x2e, 0x49, 0x9d, 0xcd, + 0x18, 0x95, 0x42, 0xab, 0xdd, 0x69, 0xb8, 0x95, 0x0c, 0x52, 0xa4, 0xd1, 0x7b, 0x05, 0x76, 0x66, + 0xbc, 0xf5, 0xd7, 0x8c, 0x9f, 0xd1, 0xe0, 0x2d, 0x69, 0x49, 0x23, 0xec, 0x19, 0xba, 0x67, 0x6c, + 0xcc, 0xcc, 0x30, 0x67, 0x00, 0x8e, 0xe5, 0xf7, 0xc3, 0xd1, 0xf1, 0x23, 0x6c, 0xec, 0xc4, 0xca, + 0x4e, 0xdc, 0xd9, 0x89, 0x4f, 0x38, 0x65, 0xc9, 0x93, 0xab, 0xb9, 0xd7, 0xfb, 0xf6, 0xc3, 0x0b, + 0x2b, 0x2a, 0x27, 0xb3, 0x1c, 0x17, 0xbc, 0x89, 0x3a, 0xef, 0xcd, 0xe7, 0x48, 0x94, 0xe7, 0xdd, + 0xd2, 0x28, 0x82, 0x48, 0xef, 0x98, 0x43, 0xb4, 0xcf, 0x2f, 0x01, 0x82, 0x0c, 0x1d, 0xbc, 0x02, + 0x06, 0x82, 0x8a, 0x77, 0x92, 0x48, 0xb0, 0xdf, 0xa0, 0xc1, 0x54, 0x0b, 0xd2, 0x97, 0x3c, 0x3a, + 0xf6, 0xf1, 0xbf, 0xb6, 0x0c, 0x1b, 0xe1, 0xc9, 0x43, 0xa5, 0x61, 0x35, 0xf7, 0x6e, 0x9b, 0x6b, + 0x30, 0xec, 0x20, 0xed, 0xda, 0x24, 0xaf, 0xaf, 0x16, 0xae, 0x75, 0xbd, 0x70, 0xad, 0x9f, 0x0b, + 0xd7, 0xfa, 0xba, 0x74, 0x7b, 0xd7, 0x4b, 0xb7, 0xf7, 0x7d, 0xe9, 0xf6, 0x3e, 0xc4, 0x5b, 0xa2, + 0xf5, 0x21, 0x47, 0x0c, 0xe4, 0x47, 0xde, 0x9e, 0x77, 0xa8, 0x86, 0xb2, 0x82, 0x36, 0xfa, 0xf4, + 0xe7, 0x1d, 0xe4, 0x03, 0xbd, 0xa9, 0x4f, 0x7f, 0x07, 0x00, 0x00, 0xff, 0xff, 0xed, 0xb2, 0xb6, + 0x7f, 0x21, 0x03, 0x00, 0x00, } func (m *ClassInfo) Marshal() (dAtA []byte, err error) { @@ -325,6 +431,76 @@ func (m *BatchInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *Params) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Params) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.CreditClassFee) > 0 { + for iNdEx := len(m.CreditClassFee) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.CreditClassFee[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *GenesisState) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *GenesisState) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Params.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + func encodeVarintTypes(dAtA []byte, offset int, v uint64) int { offset -= sovTypes(v) base := offset @@ -392,6 +568,32 @@ func (m *BatchInfo) Size() (n int) { return n } +func (m *Params) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.CreditClassFee) > 0 { + for _, e := range m.CreditClassFee { + l = e.Size() + n += 1 + l + sovTypes(uint64(l)) + } + } + return n +} + +func (m *GenesisState) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Params.Size() + n += 1 + l + sovTypes(uint64(l)) + return n +} + func sovTypes(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -796,6 +998,179 @@ func (m *BatchInfo) Unmarshal(dAtA []byte) error { } return nil } +func (m *Params) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Params: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Params: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CreditClassFee", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CreditClassFee = append(m.CreditClassFee, types.Coin{}) + if err := m.CreditClassFee[len(m.CreditClassFee)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTypes(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthTypes + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthTypes + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *GenesisState) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: GenesisState: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GenesisState: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTypes(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthTypes + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthTypes + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipTypes(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 From 6bbb05d98d3be7a21b39bd6489d149e557519f28 Mon Sep 17 00:00:00 2001 From: Ru Horlick Date: Tue, 15 Jun 2021 14:08:35 +0100 Subject: [PATCH 2/4] Update x/ecocredit/server/server_test.go Co-authored-by: likhita-809 <78951027+likhita-809@users.noreply.github.com> --- x/ecocredit/server/server_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/ecocredit/server/server_test.go b/x/ecocredit/server/server_test.go index 71a818640c..3c6d0df456 100644 --- a/x/ecocredit/server/server_test.go +++ b/x/ecocredit/server/server_test.go @@ -41,7 +41,7 @@ func TestServer(t *testing.T) { authSubspace := paramstypes.NewSubspace(cdc, amino, paramsKey, tkey, authtypes.ModuleName) bankSubspace := paramstypes.NewSubspace(cdc, amino, paramsKey, tkey, banktypes.ModuleName) - ecocreditSubspace := paramstypes.NewSubspace(cdc, amino, paramsKey, tkey, ecocredittypes.DefaultParamspace) + ecocreditSubspace := paramstypes.NewSubspace(cdc, amino, paramsKey, tkey, ecocredittypes.ModuleName) maccPerms := map[string][]string{ minttypes.ModuleName: {authtypes.Minter}, From e982646445980ae32f5be101d17a3eb9af333a18 Mon Sep 17 00:00:00 2001 From: Ru Horlick Date: Tue, 15 Jun 2021 14:09:04 +0100 Subject: [PATCH 3/4] Update x/ecocredit/server/server_test.go Co-authored-by: likhita-809 <78951027+likhita-809@users.noreply.github.com> --- x/ecocredit/server/server_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/x/ecocredit/server/server_test.go b/x/ecocredit/server/server_test.go index 3c6d0df456..b209b74053 100644 --- a/x/ecocredit/server/server_test.go +++ b/x/ecocredit/server/server_test.go @@ -33,7 +33,6 @@ func TestServer(t *testing.T) { paramsKey := sdk.NewKVStoreKey(paramstypes.StoreKey) tkey := sdk.NewTransientStoreKey(paramstypes.TStoreKey) - // baseApp.Router().AddRoute(sdk.NewRoute(banktypes.ModuleName, bank.NewHandler(bankKeeper))) baseApp.MountStore(authKey, sdk.StoreTypeIAVL) baseApp.MountStore(bankKey, sdk.StoreTypeIAVL) baseApp.MountStore(paramsKey, sdk.StoreTypeIAVL) From fccafe78557780e1178fefe3cb8832da350b9f35 Mon Sep 17 00:00:00 2001 From: Ru Horlick Date: Wed, 16 Jun 2021 11:50:42 +0100 Subject: [PATCH 4/4] Resolve review comments and add tests for genesis --- x/ecocredit/server/fee.go | 6 +++--- x/ecocredit/server/msg_server.go | 24 +++++++++++----------- x/ecocredit/server/server_test.go | 3 ++- x/ecocredit/server/testsuite/genesis.go | 27 +++++++++++++++++++++++++ x/ecocredit/server/testsuite/suite.go | 13 +++++++----- 5 files changed, 52 insertions(+), 21 deletions(-) create mode 100644 x/ecocredit/server/testsuite/genesis.go diff --git a/x/ecocredit/server/fee.go b/x/ecocredit/server/fee.go index 51c7a5a91a..08f6a704f5 100644 --- a/x/ecocredit/server/fee.go +++ b/x/ecocredit/server/fee.go @@ -6,14 +6,14 @@ import ( "github.com/regen-network/regen-ledger/x/ecocredit" ) -func (s serverImpl) GetCreditClassFee(ctx sdk.Context) sdk.Coins { +func (s serverImpl) getCreditClassFee(ctx sdk.Context) sdk.Coins { var params ecocredit.Params s.paramSpace.GetParamSet(ctx, ¶ms) return params.CreditClassFee } -func (s serverImpl) ChargeCreditClassFee(ctx sdk.Context, designerAddr sdk.AccAddress) error { - creditClassFee := s.GetCreditClassFee(ctx) +func (s serverImpl) chargeCreditClassFee(ctx sdk.Context, designerAddr sdk.AccAddress) error { + creditClassFee := s.getCreditClassFee(ctx) // Move the fee to the ecocredit module's account err := s.bankKeeper.SendCoinsFromAccountToModule(ctx, designerAddr, ecocredit.ModuleName, creditClassFee) diff --git a/x/ecocredit/server/msg_server.go b/x/ecocredit/server/msg_server.go index c335cd6c3f..34ba2be469 100644 --- a/x/ecocredit/server/msg_server.go +++ b/x/ecocredit/server/msg_server.go @@ -24,7 +24,18 @@ func (s serverImpl) CreateClass(ctx types.Context, req *ecocredit.MsgCreateClass classID := s.idSeq.NextVal(ctx) classIDStr := util.Uint64ToBase58Check(classID) - err := s.classInfoTable.Create(ctx, &ecocredit.ClassInfo{ + // Charge the designer a fee to create the credit class + designerAddress, err := sdk.AccAddressFromBech32(req.Designer) + if err != nil { + return nil, err + } + + err = s.chargeCreditClassFee(ctx.Context, designerAddress) + if err != nil { + return nil, err + } + + err = s.classInfoTable.Create(ctx, &ecocredit.ClassInfo{ ClassId: classIDStr, Designer: req.Designer, Issuers: req.Issuers, @@ -42,17 +53,6 @@ func (s serverImpl) CreateClass(ctx types.Context, req *ecocredit.MsgCreateClass return nil, err } - // Charge the designer a fee to create the credit class - designerAddress, err := sdk.AccAddressFromBech32(req.Designer) - if err != nil { - return nil, err - } - - err = s.ChargeCreditClassFee(ctx.Context, designerAddress) - if err != nil { - return nil, err - } - return &ecocredit.MsgCreateClassResponse{ClassId: classIDStr}, nil } diff --git a/x/ecocredit/server/server_test.go b/x/ecocredit/server/server_test.go index b209b74053..21d0b6fcea 100644 --- a/x/ecocredit/server/server_test.go +++ b/x/ecocredit/server/server_test.go @@ -11,8 +11,8 @@ import ( banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" paramstypes "github.com/cosmos/cosmos-sdk/x/params/types" + params "github.com/cosmos/cosmos-sdk/x/params/types/proposal" "github.com/stretchr/testify/suite" - "github.com/regen-network/regen-ledger/types/module" "github.com/regen-network/regen-ledger/types/module/server" ecocredittypes "github.com/regen-network/regen-ledger/x/ecocredit" @@ -27,6 +27,7 @@ func TestServer(t *testing.T) { amino := codec.NewLegacyAmino() authtypes.RegisterInterfaces(cdc.InterfaceRegistry()) + params.RegisterInterfaces(cdc.InterfaceRegistry()) authKey := sdk.NewKVStoreKey(authtypes.StoreKey) bankKey := sdk.NewKVStoreKey(banktypes.StoreKey) diff --git a/x/ecocredit/server/testsuite/genesis.go b/x/ecocredit/server/testsuite/genesis.go new file mode 100644 index 0000000000..088b6ccdc3 --- /dev/null +++ b/x/ecocredit/server/testsuite/genesis.go @@ -0,0 +1,27 @@ +package testsuite + +import ( + "github.com/regen-network/regen-ledger/x/ecocredit" +) + +func (s *IntegrationTestSuite) TestInitExportGenesis() { + require := s.Require() + ctx := s.sdkCtx + + // Export the default param set + exported, err := s.fixture.ExportGenesis(ctx) + require.NoError(err) + + // Set the param set to empty values to properly test init + var ecocreditParams ecocredit.Params + s.paramSpace.SetParamSet(ctx, &ecocreditParams) + + // Init the genesis data from the exported data + _, err = s.fixture.InitGenesis(ctx, exported) + require.NoError(err) + + // Check that the param set is correctly updated to the default params + s.paramSpace.GetParamSet(ctx, &ecocreditParams) + require.NoError(err) + require.Equal(ecocredit.DefaultParams().CreditClassFee, ecocreditParams.CreditClassFee) +} diff --git a/x/ecocredit/server/testsuite/suite.go b/x/ecocredit/server/testsuite/suite.go index ae415ae523..e9bf8250ab 100644 --- a/x/ecocredit/server/testsuite/suite.go +++ b/x/ecocredit/server/testsuite/suite.go @@ -9,6 +9,7 @@ import ( bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" paramstypes "github.com/cosmos/cosmos-sdk/x/params/types" + params "github.com/cosmos/cosmos-sdk/x/params/types/proposal" "github.com/stretchr/testify/suite" "github.com/regen-network/regen-ledger/types" @@ -21,11 +22,12 @@ type IntegrationTestSuite struct { fixtureFactory testutil.FixtureFactory fixture testutil.Fixture - sdkCtx sdk.Context - ctx context.Context - msgClient ecocredit.MsgClient - queryClient ecocredit.QueryClient - signers []sdk.AccAddress + sdkCtx sdk.Context + ctx context.Context + msgClient ecocredit.MsgClient + queryClient ecocredit.QueryClient + paramsQueryClient params.QueryClient + signers []sdk.AccAddress paramSpace paramstypes.Subspace bankKeeper bankkeeper.Keeper @@ -53,6 +55,7 @@ func (s *IntegrationTestSuite) SetupSuite() { s.Require().GreaterOrEqual(len(s.signers), 6) s.msgClient = ecocredit.NewMsgClient(s.fixture.TxConn()) s.queryClient = ecocredit.NewQueryClient(s.fixture.QueryConn()) + s.paramsQueryClient = params.NewQueryClient(s.fixture.QueryConn()) } func fundAccount(bankKeeper bankkeeper.Keeper, ctx sdk.Context, addr sdk.AccAddress, amounts sdk.Coins) error {