feat(simulation): Implement store decoder implementation from collections schema (#16074)
Co-authored-by: unknown unknown <unknown@unknown>
This commit is contained in:
parent
908677e648
commit
69642f6176
@ -66,7 +66,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
|
||||
* (modulemanager) [#15829](https://github.com/cosmos/cosmos-sdk/pull/15829) add new endblocker interface to handle valset updates
|
||||
* (core) [#14860](https://github.com/cosmos/cosmos-sdk/pull/14860) Add `Precommit` and `PrepareCheckState` AppModule callbacks.
|
||||
* (tx) [#15992](https://github.com/cosmos/cosmos-sdk/pull/15992) Add `WithExtensionOptions` in tx Factory to allow `SetExtensionOptions` with given extension options.
|
||||
|
||||
* (types/simulation) [#16074](https://github.com/cosmos/cosmos-sdk/pull/16074) Add generic SimulationStoreDecoder for modules using collections.
|
||||
### Improvements
|
||||
|
||||
* (client) [#16075](https://github.com/cosmos/cosmos-sdk/pull/16075) Partly revert [#15953](https://github.com/cosmos/cosmos-sdk/issues/15953) and `factory.Prepare` does nothing in offline mode.
|
||||
|
||||
@ -31,6 +31,10 @@ Ref: https://keepachangelog.com/en/1.0.0/
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Features
|
||||
|
||||
* [#16074](https://github.com/cosmos/cosmos-sdk/pull/16074) – makes the generic Collection interface public, still highly unstable.
|
||||
|
||||
## [v0.1.0](https://github.com/cosmos/cosmos-sdk/releases/tag/collections%2Fv0.1.0)
|
||||
|
||||
Collections `v0.1.0` is released! Check out the [docs](https://docs.cosmos.network/main/packages/collections) to know how to use the APIs.
|
||||
@ -74,6 +74,56 @@ type ValueCodec[T any] interface {
|
||||
ValueType() string
|
||||
}
|
||||
|
||||
// NewUntypedValueCodec returns an UntypedValueCodec for the provided ValueCodec.
|
||||
func NewUntypedValueCodec[V any](v ValueCodec[V]) UntypedValueCodec {
|
||||
typeName := fmt.Sprintf("%T", *new(V))
|
||||
checkType := func(value interface{}) (v V, err error) {
|
||||
concrete, ok := value.(V)
|
||||
if !ok {
|
||||
return v, fmt.Errorf("%w: expected value of type %s, got %T", ErrEncoding, typeName, value)
|
||||
}
|
||||
return concrete, nil
|
||||
}
|
||||
return UntypedValueCodec{
|
||||
Decode: func(b []byte) (interface{}, error) { return v.Decode(b) },
|
||||
Encode: func(value interface{}) ([]byte, error) {
|
||||
concrete, err := checkType(value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return v.Encode(concrete)
|
||||
},
|
||||
DecodeJSON: func(b []byte) (interface{}, error) {
|
||||
return v.DecodeJSON(b)
|
||||
},
|
||||
EncodeJSON: func(value interface{}) ([]byte, error) {
|
||||
concrete, err := checkType(value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return v.EncodeJSON(concrete)
|
||||
},
|
||||
Stringify: func(value interface{}) (string, error) {
|
||||
concrete, err := checkType(value)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return v.Stringify(concrete), nil
|
||||
},
|
||||
ValueType: func() string { return v.ValueType() },
|
||||
}
|
||||
}
|
||||
|
||||
// UntypedValueCodec wraps a ValueCodec to expose an untyped API for encoding and decoding values.
|
||||
type UntypedValueCodec struct {
|
||||
Decode func(b []byte) (interface{}, error)
|
||||
Encode func(value interface{}) ([]byte, error)
|
||||
DecodeJSON func(b []byte) (interface{}, error)
|
||||
EncodeJSON func(value interface{}) ([]byte, error)
|
||||
Stringify func(value interface{}) (string, error)
|
||||
ValueType func() string
|
||||
}
|
||||
|
||||
// KeyToValueCodec converts a KeyCodec into a ValueCodec.
|
||||
func KeyToValueCodec[K any](keyCodec KeyCodec[K]) ValueCodec[K] { return keyToValueCodec[K]{keyCodec} }
|
||||
|
||||
|
||||
39
collections/codec/codec_test.go
Normal file
39
collections/codec/codec_test.go
Normal file
@ -0,0 +1,39 @@
|
||||
package codec
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestUntypedValueCodec(t *testing.T) {
|
||||
vc := NewUntypedValueCodec(KeyToValueCodec(NewStringKeyCodec[string]()))
|
||||
|
||||
t.Run("encode/decode", func(t *testing.T) {
|
||||
_, err := vc.Encode(0)
|
||||
require.ErrorIs(t, err, ErrEncoding)
|
||||
b, err := vc.Encode("hello")
|
||||
require.NoError(t, err)
|
||||
value, err := vc.Decode(b)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "hello", value)
|
||||
})
|
||||
|
||||
t.Run("json encode/decode", func(t *testing.T) {
|
||||
_, err := vc.EncodeJSON(0)
|
||||
require.ErrorIs(t, err, ErrEncoding)
|
||||
b, err := vc.EncodeJSON("hello")
|
||||
require.NoError(t, err)
|
||||
value, err := vc.DecodeJSON(b)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "hello", value)
|
||||
})
|
||||
|
||||
t.Run("stringify", func(t *testing.T) {
|
||||
_, err := vc.Stringify(0)
|
||||
require.ErrorIs(t, err, ErrEncoding)
|
||||
s, err := vc.Stringify("hello")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "hello", s)
|
||||
})
|
||||
}
|
||||
@ -1,7 +1,9 @@
|
||||
package collections
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
io "io"
|
||||
"math"
|
||||
|
||||
"cosmossdk.io/collections/codec"
|
||||
@ -72,16 +74,20 @@ var (
|
||||
BytesValue = codec.KeyToValueCodec(BytesKey)
|
||||
)
|
||||
|
||||
// collection is the interface that all collections support. It will eventually
|
||||
// Collection is the interface that all collections implement. It will eventually
|
||||
// include methods for importing/exporting genesis data and schema
|
||||
// reflection for clients.
|
||||
type collection interface {
|
||||
// getName is the unique name of the collection within a schema. It must
|
||||
// NOTE: Unstable.
|
||||
type Collection interface {
|
||||
// GetName is the unique name of the collection within a schema. It must
|
||||
// match format specified by NameRegex.
|
||||
getName() string
|
||||
GetName() string
|
||||
|
||||
// getPrefix is the unique prefix of the collection within a schema.
|
||||
getPrefix() []byte
|
||||
// GetPrefix is the unique prefix of the collection within a schema.
|
||||
GetPrefix() []byte
|
||||
|
||||
// ValueCodec returns the codec used to encode/decode values of the collection.
|
||||
ValueCodec() codec.UntypedValueCodec
|
||||
|
||||
genesisHandler
|
||||
}
|
||||
@ -122,3 +128,32 @@ func NewPrefix[T interface{ int | string | []byte }](identifier T) Prefix {
|
||||
}
|
||||
return prefix
|
||||
}
|
||||
|
||||
var _ Collection = (*collectionImpl[string, string])(nil)
|
||||
|
||||
// collectionImpl wraps a Map and implements Collection. This properly splits
|
||||
// the generic and untyped Collection interface from the typed Map, which every
|
||||
// collection builds on.
|
||||
type collectionImpl[K, V any] struct {
|
||||
m Map[K, V]
|
||||
}
|
||||
|
||||
func (c collectionImpl[K, V]) ValueCodec() codec.UntypedValueCodec {
|
||||
return codec.NewUntypedValueCodec(c.m.vc)
|
||||
}
|
||||
|
||||
func (c collectionImpl[K, V]) GetName() string { return c.m.name }
|
||||
|
||||
func (c collectionImpl[K, V]) GetPrefix() []byte { return NewPrefix(c.m.prefix) }
|
||||
|
||||
func (c collectionImpl[K, V]) validateGenesis(r io.Reader) error { return c.m.validateGenesis(r) }
|
||||
|
||||
func (c collectionImpl[K, V]) importGenesis(ctx context.Context, r io.Reader) error {
|
||||
return c.m.importGenesis(ctx, r)
|
||||
}
|
||||
|
||||
func (c collectionImpl[K, V]) exportGenesis(ctx context.Context, w io.Writer) error {
|
||||
return c.m.exportGenesis(ctx, w)
|
||||
}
|
||||
|
||||
func (c collectionImpl[K, V]) defaultGenesis(w io.Writer) error { return c.m.defaultGenesis(w) }
|
||||
|
||||
@ -302,7 +302,7 @@ type KeyValue[K, V any] struct {
|
||||
|
||||
// encodeRangeBound encodes a range bound, modifying the key bytes to adhere to bound semantics.
|
||||
func encodeRangeBound[T any](prefix []byte, keyCodec codec.KeyCodec[T], bound *RangeKey[T]) ([]byte, error) {
|
||||
key, err := encodeKeyWithPrefix(prefix, keyCodec, bound.key)
|
||||
key, err := EncodeKeyWithPrefix(prefix, keyCodec, bound.key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -39,22 +39,22 @@ func NewMap[K, V any](
|
||||
prefix: prefix.Bytes(),
|
||||
name: name,
|
||||
}
|
||||
schemaBuilder.addCollection(m)
|
||||
schemaBuilder.addCollection(collectionImpl[K, V]{m})
|
||||
return m
|
||||
}
|
||||
|
||||
func (m Map[K, V]) getName() string {
|
||||
func (m Map[K, V]) GetName() string {
|
||||
return m.name
|
||||
}
|
||||
|
||||
func (m Map[K, V]) getPrefix() []byte {
|
||||
func (m Map[K, V]) GetPrefix() []byte {
|
||||
return m.prefix
|
||||
}
|
||||
|
||||
// Set maps the provided value to the provided key in the store.
|
||||
// Errors with ErrEncoding if key or value encoding fails.
|
||||
func (m Map[K, V]) Set(ctx context.Context, key K, value V) error {
|
||||
bytesKey, err := encodeKeyWithPrefix(m.prefix, m.kc, key)
|
||||
bytesKey, err := EncodeKeyWithPrefix(m.prefix, m.kc, key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -73,7 +73,7 @@ func (m Map[K, V]) Set(ctx context.Context, key K, value V) error {
|
||||
// errors with ErrNotFound if the key does not exist, or
|
||||
// with ErrEncoding if the key or value decoding fails.
|
||||
func (m Map[K, V]) Get(ctx context.Context, key K) (v V, err error) {
|
||||
bytesKey, err := encodeKeyWithPrefix(m.prefix, m.kc, key)
|
||||
bytesKey, err := EncodeKeyWithPrefix(m.prefix, m.kc, key)
|
||||
if err != nil {
|
||||
return v, err
|
||||
}
|
||||
@ -97,7 +97,7 @@ func (m Map[K, V]) Get(ctx context.Context, key K) (v V, err error) {
|
||||
// Has reports whether the key is present in storage or not.
|
||||
// Errors with ErrEncoding if key encoding fails.
|
||||
func (m Map[K, V]) Has(ctx context.Context, key K) (bool, error) {
|
||||
bytesKey, err := encodeKeyWithPrefix(m.prefix, m.kc, key)
|
||||
bytesKey, err := EncodeKeyWithPrefix(m.prefix, m.kc, key)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@ -109,7 +109,7 @@ func (m Map[K, V]) Has(ctx context.Context, key K) (bool, error) {
|
||||
// Errors with ErrEncoding if key encoding fails.
|
||||
// If the key does not exist then this is a no-op.
|
||||
func (m Map[K, V]) Remove(ctx context.Context, key K) error {
|
||||
bytesKey, err := encodeKeyWithPrefix(m.prefix, m.kc, key)
|
||||
bytesKey, err := EncodeKeyWithPrefix(m.prefix, m.kc, key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -195,7 +195,9 @@ func (m Map[K, V]) KeyCodec() codec.KeyCodec[K] { return m.kc }
|
||||
// ValueCodec returns the Map's ValueCodec.
|
||||
func (m Map[K, V]) ValueCodec() codec.ValueCodec[V] { return m.vc }
|
||||
|
||||
func encodeKeyWithPrefix[K any](prefix []byte, kc codec.KeyCodec[K], key K) ([]byte, error) {
|
||||
// EncodeKeyWithPrefix returns how the collection would store the key in storage given
|
||||
// prefix, key codec and the concrete key.
|
||||
func EncodeKeyWithPrefix[K any](prefix []byte, kc codec.KeyCodec[K], key K) ([]byte, error) {
|
||||
prefixLen := len(prefix)
|
||||
// preallocate buffer
|
||||
keyBytes := make([]byte, prefixLen+kc.Size(key))
|
||||
|
||||
@ -50,7 +50,7 @@ func TestMap_IterateRaw(t *testing.T) {
|
||||
require.NoError(t, m.Set(ctx, 2, 2))
|
||||
|
||||
// test non nil end in ascending order
|
||||
twoBigEndian, err := encodeKeyWithPrefix(nil, Uint64Key, 2)
|
||||
twoBigEndian, err := EncodeKeyWithPrefix(nil, Uint64Key, 2)
|
||||
require.NoError(t, err)
|
||||
iter, err := m.IterateRaw(ctx, nil, twoBigEndian, OrderAscending)
|
||||
require.NoError(t, err)
|
||||
@ -76,7 +76,7 @@ func Test_encodeKey(t *testing.T) {
|
||||
number := []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}
|
||||
expectedKey := append([]byte(prefix), number...)
|
||||
|
||||
gotKey, err := encodeKeyWithPrefix(NewPrefix(prefix).Bytes(), Uint64Key, 0)
|
||||
gotKey, err := EncodeKeyWithPrefix(NewPrefix(prefix).Bytes(), Uint64Key, 0)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expectedKey, gotKey)
|
||||
}
|
||||
|
||||
@ -25,8 +25,8 @@ func NewSchemaBuilderFromAccessor(accessorFunc func(ctx context.Context) store.K
|
||||
return &SchemaBuilder{
|
||||
schema: &Schema{
|
||||
storeAccessor: accessorFunc,
|
||||
collectionsByName: map[string]collection{},
|
||||
collectionsByPrefix: map[string]collection{},
|
||||
collectionsByName: map[string]Collection{},
|
||||
collectionsByPrefix: map[string]Collection{},
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -84,9 +84,9 @@ func (s *SchemaBuilder) Build() (Schema, error) {
|
||||
return schema, nil
|
||||
}
|
||||
|
||||
func (s *SchemaBuilder) addCollection(collection collection) {
|
||||
prefix := collection.getPrefix()
|
||||
name := collection.getName()
|
||||
func (s *SchemaBuilder) addCollection(collection Collection) {
|
||||
prefix := collection.GetPrefix()
|
||||
name := collection.GetName()
|
||||
|
||||
if _, ok := s.schema.collectionsByPrefix[string(prefix)]; ok {
|
||||
s.appendError(fmt.Errorf("prefix %v already taken within schema", prefix))
|
||||
@ -128,8 +128,8 @@ var nameRegex = regexp.MustCompile("^" + NameRegex + "$")
|
||||
type Schema struct {
|
||||
storeAccessor func(context.Context) store.KVStore
|
||||
collectionsOrdered []string
|
||||
collectionsByPrefix map[string]collection
|
||||
collectionsByName map[string]collection
|
||||
collectionsByPrefix map[string]Collection
|
||||
collectionsByName map[string]Collection
|
||||
}
|
||||
|
||||
// NewSchema creates a new schema for the provided KVStoreService.
|
||||
@ -157,8 +157,8 @@ func NewMemoryStoreSchema(service store.MemoryStoreService) Schema {
|
||||
func NewSchemaFromAccessor(accessor func(context.Context) store.KVStore) Schema {
|
||||
return Schema{
|
||||
storeAccessor: accessor,
|
||||
collectionsByName: map[string]collection{},
|
||||
collectionsByPrefix: map[string]collection{},
|
||||
collectionsByName: map[string]Collection{},
|
||||
collectionsByPrefix: map[string]Collection{},
|
||||
}
|
||||
}
|
||||
|
||||
@ -279,10 +279,18 @@ func (s Schema) exportGenesis(ctx context.Context, target appmodule.GenesisTarge
|
||||
return coll.exportGenesis(ctx, wc)
|
||||
}
|
||||
|
||||
func (s Schema) getCollection(name string) (collection, error) {
|
||||
func (s Schema) getCollection(name string) (Collection, error) {
|
||||
coll, ok := s.collectionsByName[name]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unknown collection: %s", name)
|
||||
}
|
||||
return coll, nil
|
||||
}
|
||||
|
||||
func (s Schema) ListCollections() []Collection {
|
||||
colls := make([]Collection, len(s.collectionsOrdered))
|
||||
for i, name := range s.collectionsOrdered {
|
||||
colls[i] = s.collectionsByName[name]
|
||||
}
|
||||
return colls
|
||||
}
|
||||
|
||||
2
go.mod
2
go.mod
@ -162,6 +162,8 @@ require (
|
||||
|
||||
// Below are the long-lived replace of the Cosmos SDK
|
||||
replace (
|
||||
// TODO: remove me after collections 0.2. is released.
|
||||
cosmossdk.io/collections => ./collections
|
||||
cosmossdk.io/core => ./core
|
||||
cosmossdk.io/store => ./store
|
||||
// TODO: remove after 0.7.0 release
|
||||
|
||||
2
go.sum
2
go.sum
@ -37,8 +37,6 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9
|
||||
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
|
||||
cosmossdk.io/api v0.4.1 h1:0ikaYM6GyxTYYcfBiyR8YnLCfhNnhKpEFnaSepCTmqg=
|
||||
cosmossdk.io/api v0.4.1/go.mod h1:jR7k5ok90LxW2lFUXvd8Vpo/dr4PpiyVegxdm7b1ZdE=
|
||||
cosmossdk.io/collections v0.1.0 h1:nzJGeiq32KnZroSrhB6rPifw4I85Cgmzw/YAmr4luv8=
|
||||
cosmossdk.io/collections v0.1.0/go.mod h1:xbauc0YsbUF8qKMVeBZl0pFCunxBIhKN/WlxpZ3lBuo=
|
||||
cosmossdk.io/depinject v1.0.0-alpha.3 h1:6evFIgj//Y3w09bqOUOzEpFj5tsxBqdc5CfkO7z+zfw=
|
||||
cosmossdk.io/depinject v1.0.0-alpha.3/go.mod h1:eRbcdQ7MRpIPEM5YUJh8k97nxHpYbc3sMUnEtt8HPWU=
|
||||
cosmossdk.io/errors v1.0.0-beta.7.0.20230429155654-3ee8242364e4 h1:rOy7iw7HlwKc5Af5qIHLXdBx/F98o6du/I/WGwOW6eA=
|
||||
|
||||
@ -209,6 +209,8 @@ replace (
|
||||
|
||||
// Below are the long-lived replace of the SimApp
|
||||
replace (
|
||||
// TODO: remove me after collections 0.2. is released.
|
||||
cosmossdk.io/collections => ../collections
|
||||
cosmossdk.io/core => ../core
|
||||
// TODO: remove after 0.7.0 release
|
||||
cosmossdk.io/x/tx => ../x/tx
|
||||
|
||||
@ -188,8 +188,6 @@ cloud.google.com/go/webrisk v1.4.0/go.mod h1:Hn8X6Zr+ziE2aNd8SliSDWpEnSS1u4R9+xX
|
||||
cloud.google.com/go/webrisk v1.5.0/go.mod h1:iPG6fr52Tv7sGk0H6qUFzmL3HHZev1htXuWDEEsqMTg=
|
||||
cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0=
|
||||
cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M=
|
||||
cosmossdk.io/collections v0.1.0 h1:nzJGeiq32KnZroSrhB6rPifw4I85Cgmzw/YAmr4luv8=
|
||||
cosmossdk.io/collections v0.1.0/go.mod h1:xbauc0YsbUF8qKMVeBZl0pFCunxBIhKN/WlxpZ3lBuo=
|
||||
cosmossdk.io/depinject v1.0.0-alpha.3 h1:6evFIgj//Y3w09bqOUOzEpFj5tsxBqdc5CfkO7z+zfw=
|
||||
cosmossdk.io/depinject v1.0.0-alpha.3/go.mod h1:eRbcdQ7MRpIPEM5YUJh8k97nxHpYbc3sMUnEtt8HPWU=
|
||||
cosmossdk.io/errors v1.0.0-beta.7.0.20230429155654-3ee8242364e4 h1:rOy7iw7HlwKc5Af5qIHLXdBx/F98o6du/I/WGwOW6eA=
|
||||
|
||||
@ -202,6 +202,8 @@ replace (
|
||||
|
||||
// Below are the long-lived replace for tests.
|
||||
replace (
|
||||
// TODO: remove me after collections v0.2.0 is released
|
||||
cosmossdk.io/collections => ../collections
|
||||
cosmossdk.io/core => ../core
|
||||
// We always want to test against the latest version of the simapp.
|
||||
cosmossdk.io/simapp => ../simapp
|
||||
|
||||
@ -190,8 +190,6 @@ cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1V
|
||||
cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M=
|
||||
cosmossdk.io/client/v2 v2.0.0-20230309163709-87da587416ba h1:LuPHCncU2KLMNPItFECs709uo46I9wSu2fAWYVCx+/U=
|
||||
cosmossdk.io/client/v2 v2.0.0-20230309163709-87da587416ba/go.mod h1:SXdwqO7cN5htalh/lhXWP8V4zKtBrhhcSTU+ytuEtmM=
|
||||
cosmossdk.io/collections v0.1.0 h1:nzJGeiq32KnZroSrhB6rPifw4I85Cgmzw/YAmr4luv8=
|
||||
cosmossdk.io/collections v0.1.0/go.mod h1:xbauc0YsbUF8qKMVeBZl0pFCunxBIhKN/WlxpZ3lBuo=
|
||||
cosmossdk.io/depinject v1.0.0-alpha.3 h1:6evFIgj//Y3w09bqOUOzEpFj5tsxBqdc5CfkO7z+zfw=
|
||||
cosmossdk.io/depinject v1.0.0-alpha.3/go.mod h1:eRbcdQ7MRpIPEM5YUJh8k97nxHpYbc3sMUnEtt8HPWU=
|
||||
cosmossdk.io/errors v1.0.0-beta.7.0.20230429155654-3ee8242364e4 h1:rOy7iw7HlwKc5Af5qIHLXdBx/F98o6du/I/WGwOW6eA=
|
||||
|
||||
@ -155,6 +155,8 @@ require (
|
||||
sigs.k8s.io/yaml v1.3.0 // indirect
|
||||
)
|
||||
|
||||
replace cosmossdk.io/collections => ../../collections
|
||||
|
||||
// Fix upstream GHSA-h395-qcrw-5vmq vulnerability.
|
||||
// TODO Remove it: https://github.com/cosmos/cosmos-sdk/issues/10409
|
||||
// TODO investigate if we can outright delete this dependency, otherwise go install won't work :(
|
||||
|
||||
@ -37,8 +37,6 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9
|
||||
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
|
||||
cosmossdk.io/api v0.4.1 h1:0ikaYM6GyxTYYcfBiyR8YnLCfhNnhKpEFnaSepCTmqg=
|
||||
cosmossdk.io/api v0.4.1/go.mod h1:jR7k5ok90LxW2lFUXvd8Vpo/dr4PpiyVegxdm7b1ZdE=
|
||||
cosmossdk.io/collections v0.1.0 h1:nzJGeiq32KnZroSrhB6rPifw4I85Cgmzw/YAmr4luv8=
|
||||
cosmossdk.io/collections v0.1.0/go.mod h1:xbauc0YsbUF8qKMVeBZl0pFCunxBIhKN/WlxpZ3lBuo=
|
||||
cosmossdk.io/core v0.6.1 h1:OBy7TI2W+/gyn2z40vVvruK3di+cAluinA6cybFbE7s=
|
||||
cosmossdk.io/core v0.6.1/go.mod h1:g3MMBCBXtxbDWBURDVnJE7XML4BG5qENhs0gzkcpuFA=
|
||||
cosmossdk.io/depinject v1.0.0-alpha.3 h1:6evFIgj//Y3w09bqOUOzEpFj5tsxBqdc5CfkO7z+zfw=
|
||||
|
||||
@ -120,3 +120,5 @@ require (
|
||||
pgregory.net/rapid v0.5.7 // indirect
|
||||
sigs.k8s.io/yaml v1.3.0 // indirect
|
||||
)
|
||||
|
||||
replace cosmossdk.io/collections => ../../collections // TODO: remove me after collections v0.2.0 is released
|
||||
|
||||
@ -39,8 +39,6 @@ cosmossdk.io/api v0.4.1 h1:0ikaYM6GyxTYYcfBiyR8YnLCfhNnhKpEFnaSepCTmqg=
|
||||
cosmossdk.io/api v0.4.1/go.mod h1:jR7k5ok90LxW2lFUXvd8Vpo/dr4PpiyVegxdm7b1ZdE=
|
||||
cosmossdk.io/client/v2 v2.0.0-20230426154441-2037a26d1235 h1:6aGhtjUgmacucrKMC9ZdF9G96YoxZqkTC2ZyxaAg1GE=
|
||||
cosmossdk.io/client/v2 v2.0.0-20230426154441-2037a26d1235/go.mod h1:ydI6QS3A+K2px6O8QpM0JtNaVV6lLeCJ5LVwtQXIMAg=
|
||||
cosmossdk.io/collections v0.1.0 h1:nzJGeiq32KnZroSrhB6rPifw4I85Cgmzw/YAmr4luv8=
|
||||
cosmossdk.io/collections v0.1.0/go.mod h1:xbauc0YsbUF8qKMVeBZl0pFCunxBIhKN/WlxpZ3lBuo=
|
||||
cosmossdk.io/core v0.6.2-0.20230323161322-ccd8d40119e4 h1:l1scDTT2VX18ZuR6P0irvT/bAP0h4297D/Lka5nz2vE=
|
||||
cosmossdk.io/core v0.6.2-0.20230323161322-ccd8d40119e4/go.mod h1:J8R0E7soOpQFVqFiFd7EKepXCPpINa2n2t2EqbEsXnY=
|
||||
cosmossdk.io/depinject v1.0.0-alpha.3 h1:6evFIgj//Y3w09bqOUOzEpFj5tsxBqdc5CfkO7z+zfw=
|
||||
|
||||
@ -141,6 +141,7 @@ require (
|
||||
|
||||
// TODO: remove after merge of https://github.com/cosmos/cosmos-sdk/pull/15873 and tagging releases
|
||||
replace (
|
||||
cosmossdk.io/collections => ../../collections // TODO: remove me after collections v0.2.0 is released
|
||||
cosmossdk.io/core => ../../core
|
||||
cosmossdk.io/store => ../../store
|
||||
cosmossdk.io/x/tx => ../../x/tx
|
||||
|
||||
@ -37,8 +37,6 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9
|
||||
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
|
||||
cosmossdk.io/api v0.4.1 h1:0ikaYM6GyxTYYcfBiyR8YnLCfhNnhKpEFnaSepCTmqg=
|
||||
cosmossdk.io/api v0.4.1/go.mod h1:jR7k5ok90LxW2lFUXvd8Vpo/dr4PpiyVegxdm7b1ZdE=
|
||||
cosmossdk.io/collections v0.1.0 h1:nzJGeiq32KnZroSrhB6rPifw4I85Cgmzw/YAmr4luv8=
|
||||
cosmossdk.io/collections v0.1.0/go.mod h1:xbauc0YsbUF8qKMVeBZl0pFCunxBIhKN/WlxpZ3lBuo=
|
||||
cosmossdk.io/depinject v1.0.0-alpha.3 h1:6evFIgj//Y3w09bqOUOzEpFj5tsxBqdc5CfkO7z+zfw=
|
||||
cosmossdk.io/depinject v1.0.0-alpha.3/go.mod h1:eRbcdQ7MRpIPEM5YUJh8k97nxHpYbc3sMUnEtt8HPWU=
|
||||
cosmossdk.io/errors v1.0.0-beta.7.0.20230429155654-3ee8242364e4 h1:rOy7iw7HlwKc5Af5qIHLXdBx/F98o6du/I/WGwOW6eA=
|
||||
|
||||
52
types/simulation/collections.go
Normal file
52
types/simulation/collections.go
Normal file
@ -0,0 +1,52 @@
|
||||
package simulation
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"cosmossdk.io/collections"
|
||||
collcodec "cosmossdk.io/collections/codec"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/types/kv"
|
||||
)
|
||||
|
||||
func NewStoreDecoderFuncFromCollectionsSchema(schema collections.Schema) func(kvA, kvB kv.Pair) string {
|
||||
colls := schema.ListCollections()
|
||||
prefixes := make([][]byte, len(colls))
|
||||
valueCodecs := make([]collcodec.UntypedValueCodec, len(colls))
|
||||
for i, coll := range colls {
|
||||
prefixes[i] = coll.GetPrefix()
|
||||
valueCodecs[i] = coll.ValueCodec()
|
||||
}
|
||||
|
||||
return func(kvA, kvB kv.Pair) string {
|
||||
for i, prefix := range prefixes {
|
||||
if bytes.HasPrefix(kvA.Key, prefix) {
|
||||
if !bytes.HasPrefix(kvB.Key, prefix) {
|
||||
panic(fmt.Sprintf("prefix mismatch, keyA has prefix %x (%s), but keyB does not %x (%s)", prefix, prefix, kvB.Key, kvB.Key))
|
||||
}
|
||||
vc := valueCodecs[i]
|
||||
// unmarshal kvA.Value to the corresponding type
|
||||
vA, err := vc.Decode(kvA.Value)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// unmarshal kvB.Value to the corresponding type
|
||||
vB, err := vc.Decode(kvB.Value)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
vAString, err := vc.Stringify(vA)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
vBString, err := vc.Stringify(vB)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return vAString + "\n" + vBString
|
||||
}
|
||||
}
|
||||
panic(fmt.Errorf("unexpected key %X (%s)", kvA.Key, kvA.Key))
|
||||
}
|
||||
}
|
||||
71
types/simulation/collections_test.go
Normal file
71
types/simulation/collections_test.go
Normal file
@ -0,0 +1,71 @@
|
||||
package simulation
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"cosmossdk.io/collections"
|
||||
"cosmossdk.io/collections/colltest"
|
||||
"github.com/cosmos/cosmos-sdk/types/kv"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestNewStoreDecoderFuncFromCollectionsSchema(t *testing.T) {
|
||||
store, _ := colltest.MockStore()
|
||||
sb := collections.NewSchemaBuilder(store)
|
||||
|
||||
prefixM1 := collections.NewPrefix("map_1")
|
||||
prefixM2 := collections.NewPrefix("map_2")
|
||||
|
||||
m1 := collections.NewMap(sb, prefixM1, "map_1", collections.StringKey, collections.StringValue)
|
||||
m2 := collections.NewMap(sb, prefixM2, "map_2", collections.Int32Key, collections.Int32Value)
|
||||
|
||||
schema, err := sb.Build()
|
||||
require.NoError(t, err)
|
||||
|
||||
// create a new store decoder function from the schema
|
||||
dec := NewStoreDecoderFuncFromCollectionsSchema(schema)
|
||||
|
||||
key1M1, err := collections.EncodeKeyWithPrefix(prefixM1, m1.KeyCodec(), "key_1")
|
||||
require.NoError(t, err)
|
||||
key2M1, err := collections.EncodeKeyWithPrefix(prefixM1, m1.KeyCodec(), "key_2")
|
||||
require.NoError(t, err)
|
||||
key1M2, err := collections.EncodeKeyWithPrefix(prefixM2, m2.KeyCodec(), int32(1))
|
||||
require.NoError(t, err)
|
||||
key2M2, err := collections.EncodeKeyWithPrefix(prefixM2, m2.KeyCodec(), int32(2))
|
||||
require.NoError(t, err)
|
||||
|
||||
storeDec1 := dec(kv.Pair{
|
||||
Key: key1M1,
|
||||
Value: []byte("value_1"),
|
||||
}, kv.Pair{
|
||||
Key: key2M1,
|
||||
Value: []byte("value_2"),
|
||||
})
|
||||
require.Equal(t, "value_1\nvalue_2", storeDec1)
|
||||
|
||||
storeDec2 := dec(kv.Pair{
|
||||
Key: key1M2,
|
||||
Value: []byte{0, 0, 0, 1},
|
||||
}, kv.Pair{
|
||||
Key: key2M2,
|
||||
Value: []byte{0, 0, 0, 2},
|
||||
})
|
||||
|
||||
require.Equal(t, "-2147483647\n-2147483646", storeDec2)
|
||||
|
||||
// test key conflict
|
||||
|
||||
require.Panics(t, func() {
|
||||
dec(
|
||||
kv.Pair{Key: append(prefixM1.Bytes(), 0x1)},
|
||||
kv.Pair{Key: append(prefixM2.Bytes(), 0x1)},
|
||||
)
|
||||
}, "must panic when keys do not have the same prefix")
|
||||
|
||||
require.Panics(t, func() {
|
||||
dec(
|
||||
kv.Pair{Key: []byte("unknown_1")},
|
||||
kv.Pair{Key: []byte("unknown_2")},
|
||||
)
|
||||
}, "must panic on unknown prefixes")
|
||||
}
|
||||
@ -172,6 +172,6 @@ func (ms multiSource) Int63() (r int64) {
|
||||
return r
|
||||
}
|
||||
|
||||
func (ms multiSource) Seed(seed int64) {
|
||||
func (ms multiSource) Seed(_ int64) {
|
||||
panic("multiSource Seed should not be called")
|
||||
}
|
||||
|
||||
@ -190,7 +190,9 @@ func (AppModule) ProposalMsgs(simState module.SimulationState) []simtypes.Weight
|
||||
}
|
||||
|
||||
// RegisterStoreDecoder registers a decoder for supply module's types
|
||||
func (am AppModule) RegisterStoreDecoder(_ simtypes.StoreDecoderRegistry) {}
|
||||
func (am AppModule) RegisterStoreDecoder(sdr simtypes.StoreDecoderRegistry) {
|
||||
sdr[types.StoreKey] = simtypes.NewStoreDecoderFuncFromCollectionsSchema(am.keeper.(keeper.BaseKeeper).Schema)
|
||||
}
|
||||
|
||||
// WeightedOperations returns the all the gov module operations with their respective weights.
|
||||
func (am AppModule) WeightedOperations(simState module.SimulationState) []simtypes.WeightedOperation {
|
||||
|
||||
@ -160,6 +160,8 @@ require (
|
||||
replace github.com/gin-gonic/gin => github.com/gin-gonic/gin v1.8.1
|
||||
|
||||
replace (
|
||||
// TODO: remove me when collections v0.2.0 is released
|
||||
cosmossdk.io/collections => ../../collections
|
||||
cosmossdk.io/core => ../../core
|
||||
cosmossdk.io/store => ../../store
|
||||
cosmossdk.io/x/tx => ../tx
|
||||
|
||||
@ -37,8 +37,6 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9
|
||||
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
|
||||
cosmossdk.io/api v0.4.1 h1:0ikaYM6GyxTYYcfBiyR8YnLCfhNnhKpEFnaSepCTmqg=
|
||||
cosmossdk.io/api v0.4.1/go.mod h1:jR7k5ok90LxW2lFUXvd8Vpo/dr4PpiyVegxdm7b1ZdE=
|
||||
cosmossdk.io/collections v0.1.0 h1:nzJGeiq32KnZroSrhB6rPifw4I85Cgmzw/YAmr4luv8=
|
||||
cosmossdk.io/collections v0.1.0/go.mod h1:xbauc0YsbUF8qKMVeBZl0pFCunxBIhKN/WlxpZ3lBuo=
|
||||
cosmossdk.io/depinject v1.0.0-alpha.3 h1:6evFIgj//Y3w09bqOUOzEpFj5tsxBqdc5CfkO7z+zfw=
|
||||
cosmossdk.io/depinject v1.0.0-alpha.3/go.mod h1:eRbcdQ7MRpIPEM5YUJh8k97nxHpYbc3sMUnEtt8HPWU=
|
||||
cosmossdk.io/errors v1.0.0-beta.7.0.20230429155654-3ee8242364e4 h1:rOy7iw7HlwKc5Af5qIHLXdBx/F98o6du/I/WGwOW6eA=
|
||||
|
||||
Loading…
Reference in New Issue
Block a user