Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(x/ecocredit): basket genesis #764

Merged
merged 16 commits into from
Feb 14, 2022
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ require (
github.com/cosmos/cosmos-proto v1.0.0-alpha7 // indirect
github.com/cosmos/cosmos-sdk/api v0.1.0-alpha4 // indirect
github.com/cosmos/cosmos-sdk/errors v1.0.0-beta.2 // indirect
github.com/cosmos/cosmos-sdk/orm v1.0.0-alpha.9 // indirect
github.com/cosmos/cosmos-sdk/orm v1.0.0-alpha.10 // indirect
github.com/cosmos/go-bip39 v1.0.0 // indirect
github.com/cosmos/iavl v0.17.1 // indirect
github.com/cosmos/ledger-cosmos-go v0.11.1 // indirect
Expand Down
3 changes: 2 additions & 1 deletion go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -218,8 +218,9 @@ github.com/cosmos/cosmos-sdk/api v0.1.0-alpha4/go.mod h1:gZu6sOu2vl4Fd7I+BjDSx2b
github.com/cosmos/cosmos-sdk/errors v1.0.0-beta.2 h1:bBglNlra8ZHb4dmbEE8V85ihLA+DkriSm7tcx6x/JWo=
github.com/cosmos/cosmos-sdk/errors v1.0.0-beta.2/go.mod h1:Gi7pzVRnvZ1N16JAXpLADzng0ePoE7YeEHaULSFB2Ts=
github.com/cosmos/cosmos-sdk/orm v1.0.0-alpha.7/go.mod h1:woV5EBAWmTaXKs7YXioQsYheXSZkmZIgLPq+Koi/GB8=
github.com/cosmos/cosmos-sdk/orm v1.0.0-alpha.9 h1:ZyGe/WnPjRgx9pHQNhWsvmPRCB3u6DZw35+YGvgI+I4=
github.com/cosmos/cosmos-sdk/orm v1.0.0-alpha.9/go.mod h1:WwRYmcnmSJpdOwr9ADwGr9gVBjYIre5TMz/nX4fStU0=
github.com/cosmos/cosmos-sdk/orm v1.0.0-alpha.10 h1:YT5p2bEI7cIXpYyniSPckptI/DctTOcMpl0vTyy5vxA=
github.com/cosmos/cosmos-sdk/orm v1.0.0-alpha.10/go.mod h1:WwRYmcnmSJpdOwr9ADwGr9gVBjYIre5TMz/nX4fStU0=
github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y=
github.com/cosmos/go-bip39 v1.0.0 h1:pcomnQdrdH22njcAatO0yWojsUnCO3y2tNoV1cb6hHY=
github.com/cosmos/go-bip39 v1.0.0/go.mod h1:RNJv0H/pOIVgxw6KS7QeX2a0Uo0aKUlfhZ4xuwvCdJw=
Expand Down
6 changes: 3 additions & 3 deletions types/testutil/fixture.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,15 @@ type Fixture interface {
// random or correspond to nodes in a test network which have keyrings.
Signers() []sdk.AccAddress

// InitGenesis initializes genesis for all modules with provided genesisData.
InitGenesis(ctx sdk.Context, genesisData map[string]json.RawMessage) (abci.ResponseInitChain, error)

// ExportGenesis returns raw encoded JSON genesis state for all modules.
ExportGenesis(ctx sdk.Context) (map[string]json.RawMessage, error)

// Codec is the app ProtoCodec.
Codec() *codec.ProtoCodec

// InitGenesis initializes genesis for all modules with provided genesisData.
InitGenesis(ctx sdk.Context, genesisData map[string]json.RawMessage) (abci.ResponseInitChain, error)

// Teardown performs any teardown actions for the fixture.
Teardown()
}
2 changes: 1 addition & 1 deletion x/ecocredit/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ require (
github.com/cosmos/cosmos-sdk v0.44.2
github.com/cosmos/cosmos-sdk/api v0.1.0-alpha4
github.com/cosmos/cosmos-sdk/errors v1.0.0-beta.2
github.com/cosmos/cosmos-sdk/orm v1.0.0-alpha.9
github.com/cosmos/cosmos-sdk/orm v1.0.0-alpha.10
github.com/gogo/protobuf v1.3.3
github.com/golang/mock v1.6.0
github.com/golang/protobuf v1.5.2
Expand Down
4 changes: 2 additions & 2 deletions x/ecocredit/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -227,8 +227,8 @@ github.com/cosmos/cosmos-sdk/api v0.1.0-alpha4/go.mod h1:gZu6sOu2vl4Fd7I+BjDSx2b
github.com/cosmos/cosmos-sdk/errors v1.0.0-beta.2 h1:bBglNlra8ZHb4dmbEE8V85ihLA+DkriSm7tcx6x/JWo=
github.com/cosmos/cosmos-sdk/errors v1.0.0-beta.2/go.mod h1:Gi7pzVRnvZ1N16JAXpLADzng0ePoE7YeEHaULSFB2Ts=
github.com/cosmos/cosmos-sdk/orm v1.0.0-alpha.7/go.mod h1:woV5EBAWmTaXKs7YXioQsYheXSZkmZIgLPq+Koi/GB8=
github.com/cosmos/cosmos-sdk/orm v1.0.0-alpha.9 h1:ZyGe/WnPjRgx9pHQNhWsvmPRCB3u6DZw35+YGvgI+I4=
github.com/cosmos/cosmos-sdk/orm v1.0.0-alpha.9/go.mod h1:WwRYmcnmSJpdOwr9ADwGr9gVBjYIre5TMz/nX4fStU0=
github.com/cosmos/cosmos-sdk/orm v1.0.0-alpha.10 h1:YT5p2bEI7cIXpYyniSPckptI/DctTOcMpl0vTyy5vxA=
github.com/cosmos/cosmos-sdk/orm v1.0.0-alpha.10/go.mod h1:WwRYmcnmSJpdOwr9ADwGr9gVBjYIre5TMz/nX4fStU0=
github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y=
github.com/cosmos/go-bip39 v1.0.0 h1:pcomnQdrdH22njcAatO0yWojsUnCO3y2tNoV1cb6hHY=
github.com/cosmos/go-bip39 v1.0.0/go.mod h1:RNJv0H/pOIVgxw6KS7QeX2a0Uo0aKUlfhZ4xuwvCdJw=
Expand Down
58 changes: 56 additions & 2 deletions x/ecocredit/module/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@ import (
"fmt"
"math/rand"

"github.com/gogo/protobuf/proto"
"google.golang.org/protobuf/reflect/protoreflect"

"github.com/gogo/protobuf/jsonpb"

"github.com/cosmos/cosmos-sdk/orm/model/ormdb"
"github.com/cosmos/cosmos-sdk/orm/types/ormjson"

baskettypes "github.com/regen-network/regen-ledger/x/ecocredit/basket"

sdkclient "github.com/cosmos/cosmos-sdk/client"
Expand Down Expand Up @@ -72,12 +80,58 @@ func (a Module) RegisterGRPCGatewayRoutes(clientCtx sdkclient.Context, mux *runt
}

func (a Module) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage {
return cdc.MustMarshalJSON(ecocredit.DefaultGenesisState())
db, err := ormdb.NewModuleDB(server.ModuleSchema, ormdb.ModuleDBOptions{})
if err != nil {
panic(err)
}

jsonTarget := ormjson.NewRawMessageTarget()
err = db.DefaultJSON(jsonTarget)
if err != nil {
panic(err)
}
aaronc marked this conversation as resolved.
Show resolved Hide resolved

err = server.MergeLegacyJSONIntoTarget(cdc, ecocredit.DefaultGenesisState(), jsonTarget)
if err != nil {
panic(err)
}

bz, err := jsonTarget.JSON()
if err != nil {
panic(err)
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like we are doing roundtrips:

  1. going form genesis structure to JSON bytes
  2. creating DB JSON
  3. deserializing json to merge the two.

Why not adding that db data to the Genesis structure (as an attribute, or an embedded field) and do only one serialization?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is true. I guess we could add a key for the legacy genesis inside the orm map before we marshal it.

But the overhead is probably not that bad. This is only for one release and then all this custom genesis stuff disappears entirely

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be hard to add a key for custom JSON inside the current GenesisState message (which is going away)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe it's worth to put some comments? I'm afraid that this can be a source of a bad practice, especially for big genesis files (or big exports).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can add a helper which does this more efficiently. This will not be the only case of hybrid modules which use orm and legacy genesis

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made this more efficient. Still not perfect, but let me know if it's good enough for now.

To do anything more efficient we'd need to nest the legacy json under a key in the orm json target, but that changes the structure even more and there are more tests to change, etc.


return bz
}

func (a Module) ValidateGenesis(cdc codec.JSONCodec, _ sdkclient.TxEncodingConfig, bz json.RawMessage) error {
db, err := ormdb.NewModuleDB(server.ModuleSchema, ormdb.ModuleDBOptions{})
if err != nil {
return err
}

jsonSource, err := ormjson.NewRawMessageSource(bz)
if err != nil {
return err
}

err = db.ValidateJSON(jsonSource)
if err != nil {
return err
}

var data ecocredit.GenesisState
if err := cdc.UnmarshalJSON(bz, &data); err != nil {

r, err := jsonSource.OpenReader(protoreflect.FullName(proto.MessageName(&data)))
if err != nil {
return err
}

if r == nil {
return nil
}

if err := (&jsonpb.Unmarshaler{AllowUnknownFields: true}).Unmarshal(r, &data); err != nil {
return fmt.Errorf("failed to unmarshal %s genesis state: %w", ecocredit.ModuleName, err)
}

Expand Down
45 changes: 45 additions & 0 deletions x/ecocredit/module/module_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package module

import (
"testing"

"github.com/cosmos/cosmos-sdk/codec"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
"github.com/stretchr/testify/require"
)

func TestHybridORMLegacyGenesis(t *testing.T) {
m := Module{}
cdc := codec.NewProtoCodec(codectypes.NewInterfaceRegistry())
bz := m.DefaultGenesis(cdc)
require.NotNil(t, bz)
require.NoError(t, m.ValidateGenesis(cdc, nil, bz))

require.NoError(t, m.ValidateGenesis(cdc, nil, []byte(`
{
"regen.ecocredit.v1alpha1.GenesisState":{
"params":{
"allowlist_enabled":true
}
}
}`)))

require.NoError(t, m.ValidateGenesis(cdc, nil, []byte(`
{
"regen.ecocredit.basket.v1.Basket":[{
"basket_denom":"foo"
}]
}`)))

require.NoError(t, m.ValidateGenesis(cdc, nil, []byte(`
{
"regen.ecocredit.v1alpha1.GenesisState":{
"params":{
"allowlist_enabled":true
}
},
"regen.ecocredit.basket.v1.Basket":[{
"basket_denom":"foo"
}]
}`)))
}
69 changes: 66 additions & 3 deletions x/ecocredit/server/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ import (
"encoding/json"
"fmt"

"github.com/gogo/protobuf/proto"

"google.golang.org/protobuf/reflect/protoreflect"

"github.com/cosmos/cosmos-sdk/orm/types/ormjson"
"github.com/gogo/protobuf/jsonpb"

"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
Expand All @@ -15,11 +22,37 @@ import (
"github.com/regen-network/regen-ledger/x/ecocredit"
)

// NOTE: currently we have ORM + non-ORM genesis in parallel. We will remove
// the non-ORM genesis soon, but for now, we merge both genesis JSON's into
// the same map.
aaronc marked this conversation as resolved.
Show resolved Hide resolved

// InitGenesis performs genesis initialization for the ecocredit module. It
// returns no validator updates.
func (s serverImpl) InitGenesis(ctx types.Context, cdc codec.Codec, data json.RawMessage) ([]abci.ValidatorUpdate, error) {
jsonSource, err := ormjson.NewRawMessageSource(data)
if err != nil {
return nil, err
}

err = s.db.ImportJSON(ctx, jsonSource)
if err != nil {
return nil, err
}

var genesisState ecocredit.GenesisState
cdc.MustUnmarshalJSON(data, &genesisState)
r, err := jsonSource.OpenReader(protoreflect.FullName(proto.MessageName(&genesisState)))
if err != nil {
return nil, err
}

if r == nil {
return nil, nil
}

err = (&jsonpb.Unmarshaler{AllowUnknownFields: true}).Unmarshal(r, &genesisState)
if err != nil {
return nil, err
}

s.paramSpace.SetParamSet(ctx.Context, &genesisState.Params)

Expand All @@ -44,7 +77,7 @@ func (s serverImpl) InitGenesis(ctx types.Context, cdc codec.Codec, data json.Ra
return nil, err
}

return []abci.ValidatorUpdate{}, nil
return nil, nil
}

// validateSupplies returns an error if credit batch genesis supply does not equal to calculated supply.
Expand Down Expand Up @@ -224,5 +257,35 @@ func (s serverImpl) ExportGenesis(ctx types.Context, cdc codec.Codec) (json.RawM
Supplies: supplies,
}

return cdc.MustMarshalJSON(gs), nil
jsonTarget := ormjson.NewRawMessageTarget()
err := s.db.ExportJSON(ctx, jsonTarget)
if err != nil {
return nil, err
}

err = MergeLegacyJSONIntoTarget(cdc, gs, jsonTarget)
if err != nil {
return nil, err
}

return jsonTarget.JSON()
}

func MergeLegacyJSONIntoTarget(cdc codec.JSONCodec, message proto.Message, target ormjson.WriteTarget) error {
aaronc marked this conversation as resolved.
Show resolved Hide resolved
w, err := target.OpenWriter(protoreflect.FullName(proto.MessageName(message)))
if err != nil {
return err
}

bz, err := cdc.MarshalJSON(message)
if err != nil {
return err
}

_, err = w.Write(bz)
if err != nil {
return err
}

return w.Close()
}
5 changes: 3 additions & 2 deletions x/ecocredit/server/ormutil/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,19 @@ package ormutil
import (
"context"

"github.com/regen-network/regen-ledger/types"

"github.com/cosmos/cosmos-sdk/orm/model/ormdb"
"github.com/cosmos/cosmos-sdk/orm/model/ormtable"
storetypes "github.com/cosmos/cosmos-sdk/store/types"
sdk "github.com/cosmos/cosmos-sdk/types"
)

// NewStoreKeyDB creates an ormdb.ModuleDB from an ormdb.ModuleDB and a StoreKey.
// It is an interim solution for using the ORM in existing Cosmos SDK modules
// before fuller integration has been done.
func NewStoreKeyDB(desc ormdb.ModuleSchema, key storetypes.StoreKey, options ormdb.ModuleDBOptions) (ormdb.ModuleDB, error) {
getBackend := func(ctx context.Context) (ormtable.Backend, error) {
sdkCtx := sdk.UnwrapSDKContext(ctx)
sdkCtx := types.UnwrapSDKContext(ctx)
store := sdkCtx.KVStore(key)
wrapper := storeWrapper{store}
return ormtable.NewBackend(ormtable.BackendOptions{
Expand Down
7 changes: 7 additions & 0 deletions x/ecocredit/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ type serverImpl struct {
batchInfoTable orm.PrimaryKeyTable

basketKeeper basket.Keeper

db ormdb.ModuleDB
}

func newServer(storeKey sdk.StoreKey, paramSpace paramtypes.Subspace,
Expand Down Expand Up @@ -66,6 +68,11 @@ func newServer(storeKey sdk.StoreKey, paramSpace paramtypes.Subspace,
}
s.batchInfoTable = batchInfoTableBuilder.Build()

s.db, err = ormutil.NewStoreKeyDB(ModuleSchema, storeKey, ormdb.ModuleDBOptions{})
if err != nil {
panic(err)
}

return s
}

Expand Down
27 changes: 23 additions & 4 deletions x/ecocredit/server/testsuite/genesis.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package testsuite

import (
"bytes"
"encoding/json"

"github.com/gogo/protobuf/jsonpb"

"github.com/regen-network/regen-ledger/types"
"github.com/regen-network/regen-ledger/types/math"
"github.com/regen-network/regen-ledger/x/ecocredit"
Expand Down Expand Up @@ -156,14 +159,23 @@ func (s *IntegrationTestSuite) TestInitExportGenesis() {

func (s *IntegrationTestSuite) exportGenesisState(ctx types.Context) ecocredit.GenesisState {
require := s.Require()
cdc := s.fixture.Codec()
exported, err := s.fixture.ExportGenesis(ctx.Context)
require.NoError(err)

var exportedGenesisState ecocredit.GenesisState
err = cdc.UnmarshalJSON(exported[ecocredit.ModuleName], &exportedGenesisState)
var wrapper map[string]json.RawMessage
err = json.Unmarshal(exported[ecocredit.ModuleName], &wrapper)
require.NoError(err)

var exportedGenesisState ecocredit.GenesisState
legacyGenesisBz, ok := wrapper["regen.ecocredit.v1alpha1.GenesisState"]
if ok {
err = (&jsonpb.Unmarshaler{AllowUnknownFields: true}).Unmarshal(
bytes.NewReader(legacyGenesisBz),
&exportedGenesisState,
)
require.NoError(err)
}

return exportedGenesisState
}

Expand All @@ -173,7 +185,14 @@ func (s *IntegrationTestSuite) initGenesisState(ctx types.Context, genesisState
genesisBytes, err := cdc.MarshalJSON(genesisState)
require.NoError(err)

genesisData := map[string]json.RawMessage{ecocredit.ModuleName: genesisBytes}
wrapper := map[string]json.RawMessage{
"regen.ecocredit.v1alpha1.GenesisState": genesisBytes,
}

bz, err := json.Marshal(wrapper)
require.NoError(err)

genesisData := map[string]json.RawMessage{ecocredit.ModuleName: bz}
_, err = s.fixture.InitGenesis(ctx.Context, genesisData)
return err
}
Expand Down