feat(collections): genesis support (#14331)
Co-authored-by: testinginprod <testinginprod@somewhere.idk> Co-authored-by: testinginprod <98415576+testinginprod@users.noreply.github.com> Co-authored-by: Marko <marbar3778@yahoo.com> Co-authored-by: Julien Robert <julien@rbrt.fr> Co-authored-by: Aleksandr Bezobchuk <alexanderbez@users.noreply.github.com> Co-authored-by: Rafael Tenfen <rafaeltenfen.rt@gmail.com> Co-authored-by: Facundo Medica <14063057+facundomedica@users.noreply.github.com> Co-authored-by: atheeshp <59333759+atheeshp@users.noreply.github.com> Co-authored-by: Likhita Polavarapu <78951027+likhita-809@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Matt Kocubinski <mkocubinski@gmail.com> Co-authored-by: Jacob Gadikian <faddat@users.noreply.github.com> Co-authored-by: samricotta <37125168+samricotta@users.noreply.github.com> Co-authored-by: Noel Ukwa <noeluchechukwu@gmail.com> Co-authored-by: JayT106 <JayT106@users.noreply.github.com> Co-authored-by: Julián Toledano <JulianToledano@users.noreply.github.com> Co-authored-by: Amaury <1293565+amaurym@users.noreply.github.com> Co-authored-by: Denver <aeharvlee@gmail.com> Co-authored-by: Hyunwoo Lee <denver@Hyunwoos-MacBook-Pro-2.local> Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> Co-authored-by: MalteHerrmann <42640438+MalteHerrmann@users.noreply.github.com> Co-authored-by: Vladislav Varadinov <vladislav.varadinov@gmail.com> Co-authored-by: cipherZ <dev@cipherz.com>
This commit is contained in:
parent
afdbc51e47
commit
b3c750ca27
@ -22,6 +22,8 @@ type collection interface {
|
||||
|
||||
// getPrefix is the unique prefix of the collection within a schema.
|
||||
getPrefix() []byte
|
||||
|
||||
genesisHandler
|
||||
}
|
||||
|
||||
// Prefix defines a segregation namespace
|
||||
@ -85,6 +87,10 @@ type KeyCodec[T any] interface {
|
||||
// to return the maximum varint bytes buffer length, at the risk of
|
||||
// over-estimating in order to pick the most performant path.
|
||||
Size(key T) int
|
||||
// EncodeJSON encodes the value as JSON.
|
||||
EncodeJSON(value T) ([]byte, error)
|
||||
// DecodeJSON decodes the provided JSON bytes into an instance of T.
|
||||
DecodeJSON(b []byte) (T, error)
|
||||
// Stringify returns a string representation of T.
|
||||
Stringify(key T) string
|
||||
// KeyType returns a string identifier for the type of the key.
|
||||
@ -117,6 +123,10 @@ type ValueCodec[T any] interface {
|
||||
Encode(value T) ([]byte, error)
|
||||
// Decode returns the type T given its binary representation.
|
||||
Decode(b []byte) (T, error)
|
||||
// EncodeJSON encodes the value as JSON.
|
||||
EncodeJSON(value T) ([]byte, error)
|
||||
// DecodeJSON decodes the provided JSON bytes into an instance of T.
|
||||
DecodeJSON(b []byte) (T, error)
|
||||
// Stringify returns a string representation of T.
|
||||
Stringify(value T) string
|
||||
// ValueType returns the identifier for the type.
|
||||
|
||||
@ -92,6 +92,13 @@ func checkKeyCodec[T any](t *testing.T, keyCodec KeyCodec[T], key T) {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, len(buffer), read, "encoded non terminal key and pair key read bytes must have same size")
|
||||
require.Equal(t, pairKey, decodedPairKey, "encoding and decoding produces different keys with non terminal encoding")
|
||||
|
||||
// check JSON
|
||||
keyJSON, err := keyCodec.EncodeJSON(key)
|
||||
require.NoError(t, err)
|
||||
decoded, err := keyCodec.DecodeJSON(keyJSON)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, key, decoded, "json encoding and decoding did not produce the same results")
|
||||
}
|
||||
|
||||
// checkValueCodec asserts the correct behaviour of a ValueCodec over the type T.
|
||||
|
||||
153
collections/genesis.go
Normal file
153
collections/genesis.go
Normal file
@ -0,0 +1,153 @@
|
||||
package collections
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
type genesisHandler interface {
|
||||
validateGenesis(r io.Reader) error
|
||||
importGenesis(ctx context.Context, r io.Reader) error
|
||||
exportGenesis(ctx context.Context, w io.Writer) error
|
||||
defaultGenesis(w io.Writer) error
|
||||
}
|
||||
|
||||
type jsonMapEntry struct {
|
||||
Key json.RawMessage `json:"key"`
|
||||
Value json.RawMessage `json:"value,omitempty"`
|
||||
}
|
||||
|
||||
func (m Map[K, V]) validateGenesis(reader io.Reader) error {
|
||||
return m.doDecodeJson(reader, func(key K, value V) error {
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (m Map[K, V]) importGenesis(ctx context.Context, reader io.Reader) error {
|
||||
return m.doDecodeJson(reader, func(key K, value V) error {
|
||||
return m.Set(ctx, key, value)
|
||||
})
|
||||
}
|
||||
|
||||
func (m Map[K, V]) exportGenesis(ctx context.Context, writer io.Writer) error {
|
||||
_, err := writer.Write([]byte("["))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
it, err := m.Iterate(ctx, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer it.Close()
|
||||
|
||||
first := true
|
||||
for ; it.Valid(); it.Next() {
|
||||
// add a comma before encoding the object
|
||||
// for all objects besides the first one.
|
||||
if !first {
|
||||
_, err = writer.Write([]byte(","))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
first = false
|
||||
|
||||
key, err := it.Key()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
keyBz, err := m.kc.EncodeJSON(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
value, err := it.Value()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
valueBz, err := m.vc.EncodeJSON(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
entry := jsonMapEntry{
|
||||
Key: keyBz,
|
||||
Value: valueBz,
|
||||
}
|
||||
|
||||
bz, err := json.Marshal(entry)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = writer.Write(bz)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
_, err = writer.Write([]byte("]"))
|
||||
return err
|
||||
}
|
||||
|
||||
func (m Map[K, V]) doDecodeJson(reader io.Reader, onEntry func(key K, value V) error) error {
|
||||
decoder := json.NewDecoder(reader)
|
||||
token, err := decoder.Token()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if token != json.Delim('[') {
|
||||
return fmt.Errorf("expected [ got %s", token)
|
||||
}
|
||||
|
||||
for decoder.More() {
|
||||
var rawJson json.RawMessage
|
||||
err := decoder.Decode(&rawJson)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var mapEntry jsonMapEntry
|
||||
err = json.Unmarshal(rawJson, &mapEntry)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
key, err := m.kc.DecodeJSON(mapEntry.Key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
value, err := m.vc.DecodeJSON(mapEntry.Value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = onEntry(key, value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
token, err = decoder.Token()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if token != json.Delim(']') {
|
||||
return fmt.Errorf("expected ] got %s", token)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m Map[K, V]) defaultGenesis(writer io.Writer) error {
|
||||
_, err := writer.Write([]byte(`[]`))
|
||||
return err
|
||||
}
|
||||
160
collections/genesis_test.go
Normal file
160
collections/genesis_test.go
Normal file
@ -0,0 +1,160 @@
|
||||
package collections
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"cosmossdk.io/core/appmodule"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestDefaultGenesis(t *testing.T) {
|
||||
f := initFixture(t)
|
||||
var writers []*bufCloser
|
||||
require.NoError(t, f.schema.DefaultGenesis(func(field string) (io.WriteCloser, error) {
|
||||
w := newBufCloser(t, "")
|
||||
writers = append(writers, w)
|
||||
return w, nil
|
||||
}))
|
||||
require.Len(t, writers, 4)
|
||||
require.Equal(t, `[]`, writers[0].Buffer.String())
|
||||
require.Equal(t, `[]`, writers[1].Buffer.String())
|
||||
require.Equal(t, `[]`, writers[2].Buffer.String())
|
||||
require.Equal(t, `[]`, writers[3].Buffer.String())
|
||||
}
|
||||
|
||||
func TestValidateGenesis(t *testing.T) {
|
||||
f := initFixture(t)
|
||||
require.NoError(t, f.schema.ValidateGenesis(createTestGenesisSource(t)))
|
||||
}
|
||||
|
||||
func TestImportGenesis(t *testing.T) {
|
||||
f := initFixture(t)
|
||||
require.NoError(t, f.schema.InitGenesis(f.ctx, createTestGenesisSource(t)))
|
||||
// assert map correct genesis
|
||||
mapIt, err := f.m.Iterate(f.ctx, nil)
|
||||
require.NoError(t, err)
|
||||
defer mapIt.Close()
|
||||
|
||||
kvs, err := mapIt.KeyValues()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, KeyValue[string, uint64]{Key: "abc", Value: 1}, kvs[0])
|
||||
require.Equal(t, KeyValue[string, uint64]{Key: "def", Value: 2}, kvs[1])
|
||||
|
||||
// assert item correct genesis
|
||||
x, err := f.i.Get(f.ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "superCoolItem", x)
|
||||
|
||||
// assert keyset correct genesis
|
||||
ksIt, err := f.ks.Iterate(f.ctx, nil)
|
||||
require.NoError(t, err)
|
||||
defer ksIt.Close()
|
||||
|
||||
keys, err := ksIt.Keys()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, []string{"0", "1", "2"}, keys)
|
||||
|
||||
// assert sequence correct genesis
|
||||
seq, err := f.s.Peek(f.ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, uint64(1000), seq)
|
||||
}
|
||||
|
||||
func TestExportGenesis(t *testing.T) {
|
||||
f := initFixture(t)
|
||||
require.NoError(t, f.schema.InitGenesis(f.ctx, createTestGenesisSource(t)))
|
||||
|
||||
var writers []*bufCloser
|
||||
require.NoError(t, f.schema.ExportGenesis(f.ctx, func(field string) (io.WriteCloser, error) {
|
||||
w := newBufCloser(t, "")
|
||||
writers = append(writers, w)
|
||||
return w, nil
|
||||
}))
|
||||
require.Len(t, writers, 4)
|
||||
require.Equal(t, expectedItemGenesis, writers[0].Buffer.String())
|
||||
require.Equal(t, expectedKeySetGenesis, writers[1].Buffer.String())
|
||||
require.Equal(t, expectedMapGenesis, writers[2].Buffer.String())
|
||||
require.Equal(t, expectedSequenceGenesis, writers[3].Buffer.String())
|
||||
}
|
||||
|
||||
type testFixture struct {
|
||||
schema Schema
|
||||
ctx context.Context
|
||||
m Map[string, uint64]
|
||||
i Item[string]
|
||||
s Sequence
|
||||
ks KeySet[string]
|
||||
}
|
||||
|
||||
func initFixture(t *testing.T) *testFixture {
|
||||
sk, ctx := deps()
|
||||
schemaBuilder := NewSchemaBuilder(sk)
|
||||
m := NewMap(schemaBuilder, NewPrefix(1), "map", StringKey, Uint64Value)
|
||||
i := NewItem(schemaBuilder, NewPrefix(2), "item", StringValue)
|
||||
s := NewSequence(schemaBuilder, NewPrefix(3), "sequence")
|
||||
ks := NewKeySet(schemaBuilder, NewPrefix(4), "key_set", StringKey)
|
||||
schema, err := schemaBuilder.Build()
|
||||
require.NoError(t, err)
|
||||
return &testFixture{
|
||||
schema: schema,
|
||||
ctx: ctx,
|
||||
m: m,
|
||||
i: i,
|
||||
s: s,
|
||||
ks: ks,
|
||||
}
|
||||
}
|
||||
|
||||
func createTestGenesisSource(t *testing.T) appmodule.GenesisSource {
|
||||
expectedOrder := []string{"item", "key_set", "map", "sequence"}
|
||||
currentIndex := 0
|
||||
return func(field string) (io.ReadCloser, error) {
|
||||
require.Equal(t, expectedOrder[currentIndex], field, "unordered genesis")
|
||||
currentIndex++
|
||||
|
||||
switch field {
|
||||
case "map":
|
||||
return newBufCloser(t, expectedMapGenesis), nil
|
||||
case "item":
|
||||
return newBufCloser(t, expectedItemGenesis), nil
|
||||
case "key_set":
|
||||
return newBufCloser(t, expectedKeySetGenesis), nil
|
||||
case "sequence":
|
||||
return newBufCloser(t, expectedSequenceGenesis), nil
|
||||
default:
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
expectedMapGenesis = `[{"key":"abc","value":"1"},{"key":"def","value":"2"}]`
|
||||
expectedItemGenesis = `[{"key":"item","value":"superCoolItem"}]`
|
||||
expectedKeySetGenesis = `[{"key":"0"},{"key":"1"},{"key":"2"}]`
|
||||
expectedSequenceGenesis = `[{"key":"item","value":"1000"}]`
|
||||
)
|
||||
|
||||
type bufCloser struct {
|
||||
*bytes.Buffer
|
||||
closed bool
|
||||
}
|
||||
|
||||
func (b *bufCloser) Close() error {
|
||||
b.closed = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func newBufCloser(t *testing.T, str string) *bufCloser {
|
||||
b := &bufCloser{
|
||||
Buffer: bytes.NewBufferString(str),
|
||||
closed: false,
|
||||
}
|
||||
// this ensures Close was called by the implementation
|
||||
t.Cleanup(func() {
|
||||
require.True(t, b.closed)
|
||||
})
|
||||
return b
|
||||
}
|
||||
@ -10,6 +10,8 @@ require (
|
||||
)
|
||||
|
||||
require (
|
||||
cosmossdk.io/api v0.2.6 // indirect
|
||||
cosmossdk.io/depinject v1.0.0-alpha.3 // indirect
|
||||
github.com/DataDog/zstd v1.4.5 // indirect
|
||||
github.com/HdrHistogram/hdrhistogram-go v1.1.2 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||
@ -18,9 +20,11 @@ require (
|
||||
github.com/cockroachdb/pebble v0.0.0-20220817183557-09c6e030a677 // indirect
|
||||
github.com/cockroachdb/redact v1.0.8 // indirect
|
||||
github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2 // indirect
|
||||
github.com/cosmos/cosmos-proto v1.0.0-beta.1 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/btree v1.1.2 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
@ -38,5 +42,9 @@ require (
|
||||
golang.org/x/exp v0.0.0-20221019170559-20944726eadf // indirect
|
||||
golang.org/x/net v0.3.0 // indirect
|
||||
golang.org/x/sys v0.3.0 // indirect
|
||||
golang.org/x/text v0.5.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6 // indirect
|
||||
google.golang.org/grpc v1.51.0 // indirect
|
||||
google.golang.org/protobuf v1.28.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
@ -1,6 +1,10 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cosmossdk.io/api v0.2.6 h1:AoNwaLLapcLsphhMK6+o0kZl+D6MMUaHVqSdwinASGU=
|
||||
cosmossdk.io/api v0.2.6/go.mod h1:u/d+GAxil0nWpl1XnQL8nkziQDIWuBDhv8VnDm/s6dI=
|
||||
cosmossdk.io/core v0.4.1 h1:5icpjPmw8J4uFp6w8OoLJPxsw6X3kRi9nAtTr3qp2Ok=
|
||||
cosmossdk.io/core v0.4.1/go.mod h1:Zqd1GB+krTF3bzhTnPNwzy3NTtXu+MKLX/9sPQXTIDE=
|
||||
cosmossdk.io/depinject v1.0.0-alpha.3 h1:6evFIgj//Y3w09bqOUOzEpFj5tsxBqdc5CfkO7z+zfw=
|
||||
cosmossdk.io/depinject v1.0.0-alpha.3/go.mod h1:eRbcdQ7MRpIPEM5YUJh8k97nxHpYbc3sMUnEtt8HPWU=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
@ -16,6 +20,7 @@ github.com/Joker/jade v1.0.1-0.20190614124447-d475f43051e7/go.mod h1:6E6s8o2AE4K
|
||||
github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0=
|
||||
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
||||
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
|
||||
github.com/alecthomas/participle/v2 v2.0.0-alpha7 h1:cK4vjj0VSgb3lN1nuKA5F7dw+1s1pWBe5bx7nNCnN+c=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
@ -24,6 +29,7 @@ github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cb
|
||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cockroachdb/apd/v3 v3.1.0 h1:MK3Ow7LH0W8zkd5GMKA1PvS9qG3bWFI95WaVNfyZJ/w=
|
||||
github.com/cockroachdb/datadriven v1.0.0/go.mod h1:5Ib8Meh+jk1RlHIXej6Pzevx/NLlNvQB9pmSBZErGA4=
|
||||
github.com/cockroachdb/errors v1.6.1/go.mod h1:tm6FTP5G81vwJ5lC0SizQo374JNCOPrHyXGitRJoDqM=
|
||||
github.com/cockroachdb/errors v1.8.1 h1:A5+txlVZfOqFBDa4mGz2bUWSp0aHElvHX2bKkdbQu+Y=
|
||||
@ -42,8 +48,12 @@ github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8Nz
|
||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/cosmos/cosmos-db v0.0.0-20221226095112-f3c38ecb5e32 h1:zlCp9n3uwQieELltZWHRmwPmPaZ8+XoL2Sj+A2YJlr8=
|
||||
github.com/cosmos/cosmos-db v0.0.0-20221226095112-f3c38ecb5e32/go.mod h1:kwMlEC4wWvB48zAShGKVqboJL6w4zCLesaNQ3YLU2BQ=
|
||||
github.com/cosmos/cosmos-proto v1.0.0-beta.1 h1:iDL5qh++NoXxG8hSy93FdYJut4XfgbShIocllGaXx/0=
|
||||
github.com/cosmos/cosmos-proto v1.0.0-beta.1/go.mod h1:8k2GNZghi5sDRFw/scPL8gMSowT1vDA+5ouxL8GjaUE=
|
||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/cucumber/common/gherkin/go/v22 v22.0.0 h1:4K8NqptbvdOrjL9DEea6HFjSpbdT9+Q5kgLpmmsHYl0=
|
||||
github.com/cucumber/common/messages/go/v17 v17.1.1 h1:RNqopvIFyLWnKv0LfATh34SWBhXeoFTJnSrgm9cT/Ts=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@ -79,6 +89,7 @@ github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg78
|
||||
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
|
||||
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
|
||||
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
|
||||
github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0=
|
||||
github.com/gogo/googleapis v0.0.0-20180223154316-0cd9801be74a/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
|
||||
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||
@ -98,6 +109,9 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
@ -109,6 +123,7 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
@ -177,7 +192,6 @@ github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOA
|
||||
github.com/nats-io/nats.go v1.8.1/go.mod h1:BrFz9vVn0fU3AcH9Vn4Kd7W0NpJ651tD5omQ3M8LwxM=
|
||||
github.com/nats-io/nkeys v0.0.2/go.mod h1:dab7URMsZm6Z/jp9Z5UGa87Uutgc2mVpXLC4B7TDb/4=
|
||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
@ -202,6 +216,7 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/regen-network/gocuke v0.6.2 h1:pHviZ0kKAq2U2hN2q3smKNxct6hS0mGByFMHGnWA97M=
|
||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg=
|
||||
github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
|
||||
@ -329,6 +344,7 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM=
|
||||
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
@ -360,22 +376,30 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
|
||||
google.golang.org/genproto v0.0.0-20180518175338-11a468237815/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6 h1:a2S6M0+660BgMNl++4JPlcAO/CjkqYItDEZwkoDQK7c=
|
||||
google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg=
|
||||
google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||
google.golang.org/grpc v1.51.0 h1:E1eGv1FTqoLIdnBCZufiSHgKjlqG6fKFf6pPWtMTh8U=
|
||||
google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
|
||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
|
||||
@ -389,6 +413,8 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
pgregory.net/rapid v0.5.3 h1:163N50IHFqr1phZens4FQOdPgfJscR7a562mjQqeo4M=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
package collections
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Item is a type declaration based on Map
|
||||
@ -17,8 +19,7 @@ func NewItem[V any](
|
||||
name string,
|
||||
valueCodec ValueCodec[V],
|
||||
) Item[V] {
|
||||
item := (Item[V])(newMap[noKey](schema, prefix, name, noKey{}, valueCodec))
|
||||
schema.addCollection(item)
|
||||
item := (Item[V])(NewMap[noKey](schema, prefix, name, noKey{}, valueCodec))
|
||||
return item
|
||||
}
|
||||
|
||||
@ -55,11 +56,18 @@ func (i Item[V]) Remove(ctx context.Context) error {
|
||||
// noKey defines a KeyCodec which decodes nothing.
|
||||
type noKey struct{}
|
||||
|
||||
func (noKey) Stringify(_ noKey) string { return "no_key" }
|
||||
func (noKey) KeyType() string { return "no_key" }
|
||||
func (noKey) Size(_ noKey) int { return 0 }
|
||||
func (noKey) Encode(_ []byte, _ noKey) (int, error) { return 0, nil }
|
||||
func (noKey) Decode(_ []byte) (int, noKey, error) { return 0, noKey{}, nil }
|
||||
func (noKey) Stringify(_ noKey) string { return "no_key" }
|
||||
func (noKey) KeyType() string { return "no_key" }
|
||||
func (noKey) Size(_ noKey) int { return 0 }
|
||||
func (noKey) Encode(_ []byte, _ noKey) (int, error) { return 0, nil }
|
||||
func (noKey) Decode(_ []byte) (int, noKey, error) { return 0, noKey{}, nil }
|
||||
func (noKey) EncodeJSON(_ noKey) ([]byte, error) { return []byte(`"item"`), nil }
|
||||
func (noKey) DecodeJSON(b []byte) (noKey, error) {
|
||||
if !bytes.Equal(b, []byte(`"item"`)) {
|
||||
return noKey{}, fmt.Errorf("%w: invalid item json key bytes", ErrEncoding)
|
||||
}
|
||||
return noKey{}, nil
|
||||
}
|
||||
func (k noKey) EncodeNonTerminal(_ []byte, _ noKey) (int, error) { panic("must not be called") }
|
||||
func (k noKey) DecodeNonTerminal(_ []byte) (int, noKey, error) { panic("must not be called") }
|
||||
func (k noKey) SizeNonTerminal(_ noKey) int { panic("must not be called") }
|
||||
|
||||
@ -2,9 +2,10 @@ package collections
|
||||
|
||||
import (
|
||||
"context"
|
||||
"cosmossdk.io/core/store"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"cosmossdk.io/core/store"
|
||||
)
|
||||
|
||||
// ErrInvalidIterator is returned when an Iterate call resulted in an invalid iterator.
|
||||
|
||||
@ -3,6 +3,7 @@ package collections
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
@ -37,6 +38,14 @@ func (uint64Key) Decode(buffer []byte) (int, uint64, error) {
|
||||
return 8, binary.BigEndian.Uint64(buffer), nil
|
||||
}
|
||||
|
||||
func (uint64Key) EncodeJSON(value uint64) ([]byte, error) {
|
||||
return uint64EncodeJSON(value)
|
||||
}
|
||||
|
||||
func (uint64Key) DecodeJSON(b []byte) (uint64, error) {
|
||||
return uint64DecodeJSON(b)
|
||||
}
|
||||
|
||||
func (uint64Key) Size(_ uint64) int { return 8 }
|
||||
|
||||
func (u uint64Key) EncodeNonTerminal(buffer []byte, key uint64) (int, error) {
|
||||
@ -69,6 +78,16 @@ func (stringKey) Decode(buffer []byte) (int, string, error) {
|
||||
return len(buffer), string(buffer), nil
|
||||
}
|
||||
|
||||
func (stringKey) EncodeJSON(value string) ([]byte, error) {
|
||||
return json.Marshal(value)
|
||||
}
|
||||
|
||||
func (stringKey) DecodeJSON(b []byte) (string, error) {
|
||||
var value string
|
||||
err := json.Unmarshal(b, &value)
|
||||
return value, err
|
||||
}
|
||||
|
||||
func (stringKey) Size(key string) int {
|
||||
return len(key)
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@ import (
|
||||
)
|
||||
|
||||
func TestUint64Key(t *testing.T) {
|
||||
t.Run("bijective", func(t *testing.T) {
|
||||
t.Run("correctness", func(t *testing.T) {
|
||||
checkKeyCodec(t, Uint64Key, 55)
|
||||
})
|
||||
|
||||
|
||||
@ -61,6 +61,17 @@ const noValueValueType = "no_value"
|
||||
|
||||
type noValue struct{}
|
||||
|
||||
func (n noValue) EncodeJSON(_ noValue) ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (n noValue) DecodeJSON(b []byte) (noValue, error) {
|
||||
if b != nil {
|
||||
return noValue{}, fmt.Errorf("%w: expected nil json bytes, got: %x", ErrEncoding, b)
|
||||
}
|
||||
return noValue{}, nil
|
||||
}
|
||||
|
||||
func (noValue) Encode(_ noValue) ([]byte, error) {
|
||||
return []byte{}, nil
|
||||
}
|
||||
|
||||
@ -30,22 +30,15 @@ func NewMap[K, V any](
|
||||
keyCodec KeyCodec[K],
|
||||
valueCodec ValueCodec[V],
|
||||
) Map[K, V] {
|
||||
m := newMap(schemaBuilder, prefix, name, keyCodec, valueCodec)
|
||||
schemaBuilder.addCollection(m)
|
||||
return m
|
||||
}
|
||||
|
||||
func newMap[K, V any](
|
||||
schemaBuilder *SchemaBuilder, prefix Prefix, name string,
|
||||
keyCodec KeyCodec[K], valueCodec ValueCodec[V],
|
||||
) Map[K, V] {
|
||||
return Map[K, V]{
|
||||
m := Map[K, V]{
|
||||
kc: keyCodec,
|
||||
vc: valueCodec,
|
||||
sa: schemaBuilder.schema.storeAccessor,
|
||||
prefix: prefix.Bytes(),
|
||||
name: name,
|
||||
}
|
||||
schemaBuilder.addCollection(m)
|
||||
return m
|
||||
}
|
||||
|
||||
func (m Map[K, V]) getName() string {
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package collections
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
@ -174,6 +175,41 @@ func (p pairKeyCodec[K1, K2]) SizeNonTerminal(key Pair[K1, K2]) int {
|
||||
return size
|
||||
}
|
||||
|
||||
// GENESIS
|
||||
|
||||
type jsonPairKey [2]json.RawMessage
|
||||
|
||||
func (p pairKeyCodec[K1, K2]) EncodeJSON(v Pair[K1, K2]) ([]byte, error) {
|
||||
k1Json, err := p.keyCodec1.EncodeJSON(v.K1())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
k2Json, err := p.keyCodec2.EncodeJSON(v.K2())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return json.Marshal(jsonPairKey{k1Json, k2Json})
|
||||
}
|
||||
|
||||
func (p pairKeyCodec[K1, K2]) DecodeJSON(b []byte) (Pair[K1, K2], error) {
|
||||
pairJSON := jsonPairKey{}
|
||||
err := json.Unmarshal(b, &pairJSON)
|
||||
if err != nil {
|
||||
return Pair[K1, K2]{}, err
|
||||
}
|
||||
|
||||
k1, err := p.keyCodec1.DecodeJSON(pairJSON[0])
|
||||
if err != nil {
|
||||
return Pair[K1, K2]{}, err
|
||||
}
|
||||
k2, err := p.keyCodec2.DecodeJSON(pairJSON[1])
|
||||
if err != nil {
|
||||
return Pair[K1, K2]{}, err
|
||||
}
|
||||
|
||||
return Join(k1, k2), nil
|
||||
}
|
||||
|
||||
// NewPrefixedPairRange creates a new PairRange which will prefix over all the keys
|
||||
// starting with the provided prefix.
|
||||
func NewPrefixedPairRange[K1, K2 any](prefix K1) *PairRange[K1, K2] {
|
||||
|
||||
@ -19,6 +19,12 @@ func TestPair(t *testing.T) {
|
||||
s = keyCodec.Stringify(Pair[string, string]{})
|
||||
require.Equal(t, `(<nil>, <nil>)`, s)
|
||||
})
|
||||
|
||||
t.Run("json", func(t *testing.T) {
|
||||
b, err := keyCodec.EncodeJSON(Join("k1", "k2"))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, []byte(`["k1","k2"]`), b)
|
||||
})
|
||||
}
|
||||
|
||||
func TestPairRange(t *testing.T) {
|
||||
|
||||
@ -4,8 +4,11 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"cosmossdk.io/core/appmodule"
|
||||
|
||||
"cosmossdk.io/core/store"
|
||||
|
||||
"github.com/hashicorp/go-multierror"
|
||||
@ -58,9 +61,17 @@ func (s *SchemaBuilder) Build() (Schema, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// compute ordered collections
|
||||
collectionsOrdered := make([]string, 0, len(s.schema.collectionsByName))
|
||||
for name := range s.schema.collectionsByName {
|
||||
collectionsOrdered = append(collectionsOrdered, name)
|
||||
}
|
||||
sort.Strings(collectionsOrdered)
|
||||
s.schema.collectionsOrdered = collectionsOrdered
|
||||
|
||||
if s.schema == nil {
|
||||
// explicit panic to avoid nil pointer dereference
|
||||
panic("schema is nil")
|
||||
panic("builder already used to construct a schema")
|
||||
}
|
||||
|
||||
schema := *s.schema
|
||||
@ -105,6 +116,7 @@ var nameRegex = regexp.MustCompile("^" + NameRegex + "$")
|
||||
// clients.
|
||||
type Schema struct {
|
||||
storeAccessor func(context.Context) store.KVStore
|
||||
collectionsOrdered []string
|
||||
collectionsByPrefix map[string]collection
|
||||
collectionsByName map[string]collection
|
||||
}
|
||||
@ -138,3 +150,148 @@ func NewSchemaFromAccessor(accessor func(context.Context) store.KVStore) Schema
|
||||
collectionsByPrefix: map[string]collection{},
|
||||
}
|
||||
}
|
||||
|
||||
func (s Schema) addCollection(collection collection) {
|
||||
prefix := collection.getPrefix()
|
||||
name := collection.getName()
|
||||
|
||||
if _, ok := s.collectionsByPrefix[string(prefix)]; ok {
|
||||
panic(fmt.Errorf("prefix %v already taken within schema", prefix))
|
||||
}
|
||||
|
||||
if _, ok := s.collectionsByName[name]; ok {
|
||||
panic(fmt.Errorf("name %s already taken within schema", name))
|
||||
}
|
||||
|
||||
if !nameRegex.MatchString(name) {
|
||||
panic(fmt.Errorf("name must match regex %s, got %s", NameRegex, name))
|
||||
}
|
||||
|
||||
s.collectionsByPrefix[string(prefix)] = collection
|
||||
s.collectionsByName[name] = collection
|
||||
}
|
||||
|
||||
// DefaultGenesis implements the appmodule.HasGenesis.DefaultGenesis method.
|
||||
func (s Schema) DefaultGenesis(target appmodule.GenesisTarget) error {
|
||||
for _, name := range s.collectionsOrdered {
|
||||
err := s.defaultGenesis(target, name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to instantiate default genesis for %s: %w", name, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s Schema) defaultGenesis(target appmodule.GenesisTarget, name string) error {
|
||||
wc, err := target(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer wc.Close()
|
||||
|
||||
coll, err := s.getCollection(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return coll.defaultGenesis(wc)
|
||||
}
|
||||
|
||||
// ValidateGenesis implements the appmodule.HasGenesis.ValidateGenesis method.
|
||||
func (s Schema) ValidateGenesis(source appmodule.GenesisSource) error {
|
||||
for _, name := range s.collectionsOrdered {
|
||||
err := s.validateGenesis(source, name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed genesis validation of %s: %w", name, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s Schema) validateGenesis(source appmodule.GenesisSource, name string) error {
|
||||
rc, err := source(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rc.Close()
|
||||
|
||||
coll, err := s.getCollection(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = coll.validateGenesis(rc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// InitGenesis implements the appmodule.HasGenesis.InitGenesis method.
|
||||
func (s Schema) InitGenesis(ctx context.Context, source appmodule.GenesisSource) error {
|
||||
for _, name := range s.collectionsOrdered {
|
||||
err := s.initGenesis(ctx, source, name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed genesis initialisation of %s: %w", name, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s Schema) initGenesis(ctx context.Context, source appmodule.GenesisSource, name string) error {
|
||||
rc, err := source(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rc.Close()
|
||||
|
||||
coll, err := s.getCollection(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = coll.importGenesis(ctx, rc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ExportGenesis implements the appmodule.HasGenesis.ExportGenesis method.
|
||||
func (s Schema) ExportGenesis(ctx context.Context, target appmodule.GenesisTarget) error {
|
||||
for _, name := range s.collectionsOrdered {
|
||||
err := s.exportGenesis(ctx, target, name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to export genesis for %s: %w", name, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s Schema) exportGenesis(ctx context.Context, target appmodule.GenesisTarget, name string) error {
|
||||
wc, err := target(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer wc.Close()
|
||||
|
||||
coll, err := s.getCollection(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return coll.exportGenesis(ctx, wc)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@ -2,10 +2,18 @@ package collections
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var Uint64Value ValueCodec[uint64] = uint64Value{}
|
||||
var (
|
||||
// Uint64Value implements a ValueCodec for uint64. It converts the uint64 to big endian bytes.
|
||||
// The JSON representation is the string format of uint64.
|
||||
Uint64Value ValueCodec[uint64] = uint64Value{}
|
||||
// StringValue implements a ValueCodec for string.
|
||||
StringValue ValueCodec[string] = stringValue{}
|
||||
)
|
||||
|
||||
type uint64Value struct{}
|
||||
|
||||
@ -27,3 +35,56 @@ func (u uint64Value) Stringify(value uint64) string {
|
||||
func (u uint64Value) ValueType() string {
|
||||
return Uint64Key.KeyType()
|
||||
}
|
||||
|
||||
func (u uint64Value) EncodeJSON(value uint64) ([]byte, error) {
|
||||
return uint64EncodeJSON(value)
|
||||
}
|
||||
|
||||
func (u uint64Value) DecodeJSON(b []byte) (uint64, error) {
|
||||
return uint64DecodeJSON(b)
|
||||
}
|
||||
|
||||
func uint64EncodeJSON(value uint64) ([]byte, error) {
|
||||
str := `"` + strconv.FormatUint(value, 10) + `"`
|
||||
return []byte(str), nil
|
||||
}
|
||||
|
||||
func uint64DecodeJSON(b []byte) (uint64, error) {
|
||||
var str string
|
||||
err := json.Unmarshal(b, &str)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return strconv.ParseUint(str, 10, 64)
|
||||
}
|
||||
|
||||
type stringValue struct{}
|
||||
|
||||
func (stringValue) Encode(value string) ([]byte, error) {
|
||||
return []byte(value), nil
|
||||
}
|
||||
|
||||
func (stringValue) Decode(b []byte) (string, error) {
|
||||
return string(b), nil
|
||||
}
|
||||
|
||||
func (stringValue) EncodeJSON(value string) ([]byte, error) {
|
||||
return json.Marshal(value)
|
||||
}
|
||||
|
||||
func (stringValue) DecodeJSON(b []byte) (string, error) {
|
||||
var s string
|
||||
err := json.Unmarshal(b, &s)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func (stringValue) Stringify(value string) string {
|
||||
return value
|
||||
}
|
||||
|
||||
func (stringValue) ValueType() string {
|
||||
return "string"
|
||||
}
|
||||
|
||||
@ -16,3 +16,13 @@ func TestUint64Value(t *testing.T) {
|
||||
require.ErrorIs(t, err, ErrEncoding)
|
||||
})
|
||||
}
|
||||
|
||||
func TestUInt64JSON(t *testing.T) {
|
||||
var x uint64 = 3076
|
||||
bz, err := uint64EncodeJSON(x)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, []byte(`"3076"`), bz)
|
||||
y, err := uint64DecodeJSON(bz)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, x, y)
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user