From b930513083e8485981878f9763bd3b49a25c3785 Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Mon, 7 Oct 2024 09:43:10 +0200 Subject: [PATCH 01/24] feat(tm2/sdk/params): initial version --- tm2/pkg/sdk/auth/keeper.go | 12 +- tm2/pkg/sdk/bank/keeper.go | 4 +- tm2/pkg/sdk/params/consts.go | 6 + tm2/pkg/sdk/params/doc.go | 13 ++ tm2/pkg/sdk/params/handler.go | 83 ++++++++++++ tm2/pkg/sdk/params/keeper.go | 151 +++++++++++++++++++++ tm2/pkg/sdk/params/keeper_test.go | 209 ++++++++++++++++++++++++++++++ tm2/pkg/sdk/params/paramset.go | 25 ++++ tm2/pkg/sdk/params/table.go | 71 ++++++++++ tm2/pkg/sdk/params/test_common.go | 46 +++++++ tm2/pkg/store/README.md | 12 -- 11 files changed, 612 insertions(+), 20 deletions(-) create mode 100644 tm2/pkg/sdk/params/consts.go create mode 100644 tm2/pkg/sdk/params/doc.go create mode 100644 tm2/pkg/sdk/params/handler.go create mode 100644 tm2/pkg/sdk/params/keeper.go create mode 100644 tm2/pkg/sdk/params/keeper_test.go create mode 100644 tm2/pkg/sdk/params/paramset.go create mode 100644 tm2/pkg/sdk/params/table.go create mode 100644 tm2/pkg/sdk/params/test_common.go diff --git a/tm2/pkg/sdk/auth/keeper.go b/tm2/pkg/sdk/auth/keeper.go index e43b5389844..7669b8ace73 100644 --- a/tm2/pkg/sdk/auth/keeper.go +++ b/tm2/pkg/sdk/auth/keeper.go @@ -31,11 +31,6 @@ func NewAccountKeeper( } } -// Logger returns a module-specific logger. -func (ak AccountKeeper) Logger(ctx sdk.Context) *slog.Logger { - return ctx.Logger().With("module", fmt.Sprintf("auth")) -} - // NewAccountWithAddress implements AccountKeeper. func (ak AccountKeeper) NewAccountWithAddress(ctx sdk.Context, addr crypto.Address) std.Account { acc := ak.proto() @@ -53,7 +48,12 @@ func (ak AccountKeeper) NewAccountWithAddress(ctx sdk.Context, addr crypto.Addre return acc } -// GetAccount implements AccountKeeper. +// Logger returns a module-specific logger. +func (ak AccountKeeper) Logger(ctx sdk.Context) *slog.Logger { + return ctx.Logger().With("module", ModuleName) +} + +// GetAccount returns a specific account in the AccountKeeper. func (ak AccountKeeper) GetAccount(ctx sdk.Context, addr crypto.Address) std.Account { stor := ctx.Store(ak.key) bz := stor.Get(AddressStoreKey(addr)) diff --git a/tm2/pkg/sdk/bank/keeper.go b/tm2/pkg/sdk/bank/keeper.go index 5d3699c99ef..f98e6b3e225 100644 --- a/tm2/pkg/sdk/bank/keeper.go +++ b/tm2/pkg/sdk/bank/keeper.go @@ -25,8 +25,8 @@ type BankKeeperI interface { var _ BankKeeperI = BankKeeper{} -// BBankKeeper only allows transfers between accounts without the possibility of -// creating coins. It implements the BankKeeper interface. +// BankKeeper only allows transfers between accounts without the possibility of +// creating coins. It implements the BankKeeperI interface. type BankKeeper struct { ViewKeeper diff --git a/tm2/pkg/sdk/params/consts.go b/tm2/pkg/sdk/params/consts.go new file mode 100644 index 00000000000..eb829a08b73 --- /dev/null +++ b/tm2/pkg/sdk/params/consts.go @@ -0,0 +1,6 @@ +package params + +const ( + ModuleName = "params" + StoreKey = ModuleName +) diff --git a/tm2/pkg/sdk/params/doc.go b/tm2/pkg/sdk/params/doc.go new file mode 100644 index 00000000000..bf7449adb49 --- /dev/null +++ b/tm2/pkg/sdk/params/doc.go @@ -0,0 +1,13 @@ +// Package params provides a lightweight implementation inspired by the x/params +// module of the Cosmos SDK. +// +// It includes a keeper for managing key-value pairs with module identifiers as +// prefixes, along with a global querier for retrieving any key from any module. +// +// Changes: This version removes the concepts of subspaces and proposals, +// allowing the creation of multiple keepers identified by a provided prefix. +// Proposals may be added later when governance modules are introduced. The +// transient store and .Modified helper have also been removed but can be +// implemented later if needed. Keys are represented as strings instead of +// []byte. +package params diff --git a/tm2/pkg/sdk/params/handler.go b/tm2/pkg/sdk/params/handler.go new file mode 100644 index 00000000000..22b6961bd47 --- /dev/null +++ b/tm2/pkg/sdk/params/handler.go @@ -0,0 +1,83 @@ +package params + +import ( + "fmt" + "strings" + + abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types" + "github.com/gnolang/gno/tm2/pkg/sdk" + "github.com/gnolang/gno/tm2/pkg/std" +) + +type paramsHandler struct { + params ParamsKeeper +} + +func NewHandler(params ParamsKeeper) paramsHandler { + return paramsHandler{ + params: params, + } +} + +func (bh paramsHandler) Process(ctx sdk.Context, msg std.Msg) sdk.Result { + errMsg := fmt.Sprintf("unrecognized params message type: %T", msg) + return abciResult(std.ErrUnknownRequest(errMsg)) +} + +//---------------------------------------- +// Query + +const QueryParams = "params" + +func (bh paramsHandler) Query(ctx sdk.Context, req abci.RequestQuery) (res abci.ResponseQuery) { + switch secondPart(req.Path) { + case QueryParams: + return bh.queryParam(ctx, req) + default: + res = sdk.ABCIResponseQueryFromError( + std.ErrUnknownRequest("unknown params query endpoint")) + return + } +} + +// queryParam returns param for a key. +func (bh paramsHandler) queryParam(ctx sdk.Context, req abci.RequestQuery) (res abci.ResponseQuery) { + // parse key from path. + key := thirdPartWithSlashes(req.Path) + if key == "" { + res = sdk.ABCIResponseQueryFromError( + std.ErrUnknownRequest("param key is empty")) + } + + // XXX: validate + + panic("not implemented") + + return +} + +//---------------------------------------- +// misc + +func abciResult(err error) sdk.Result { + return sdk.ABCIResultFromError(err) +} + +// returns the second component of a path. +func secondPart(path string) string { + parts := strings.Split(path, "/") + if len(parts) < 2 { + return "" + } else { + return parts[1] + } +} + +// returns the third component of a path, including other slashes. +func thirdPartWithSlashes(path string) string { + secondSlash := strings.Index(path[strings.Index(path, "/")+1:], "/") + if secondSlash == -1 { + return "" // Return original if less than two slashes + } + return path[strings.Index(path, "/")+secondSlash+1:] +} diff --git a/tm2/pkg/sdk/params/keeper.go b/tm2/pkg/sdk/params/keeper.go new file mode 100644 index 00000000000..da1283bd5cf --- /dev/null +++ b/tm2/pkg/sdk/params/keeper.go @@ -0,0 +1,151 @@ +package params + +import ( + "fmt" + "log/slog" + "maps" + "reflect" + + "github.com/gnolang/gno/tm2/pkg/amino" + "github.com/gnolang/gno/tm2/pkg/sdk" + "github.com/gnolang/gno/tm2/pkg/store" +) + +// global paramstore Keeper. +type ParamsKeeper struct { + key store.StoreKey + table KeyTable + prefix string +} + +// NewParamsKeeper returns a new ParamsKeeper. +func NewParamsKeeper(key store.StoreKey, prefix string) ParamsKeeper { + return ParamsKeeper{ + key: key, + table: NewKeyTable(), + prefix: prefix, + } +} + +// Logger returns a module-specific logger. +// XXX: why do we expose this? +func (pk ParamsKeeper) Logger(ctx sdk.Context) *slog.Logger { + return ctx.Logger().With("module", ModuleName) +} + +func (pk ParamsKeeper) Has(ctx sdk.Context, key string) bool { + stor := ctx.Store(pk.key) + return stor.Has([]byte(key)) +} + +func (pk ParamsKeeper) Get(ctx sdk.Context, key string, ptr interface{}) { + pk.checkType(key, ptr) + stor := ctx.Store(pk.key) + bz := stor.Get([]byte(key)) + err := amino.UnmarshalJSON(bz, ptr) + if err != nil { + panic(err) + } +} + +func (pk ParamsKeeper) GetIfExists(ctx sdk.Context, key string, ptr interface{}) { + stor := ctx.Store(pk.key) + bz := stor.Get([]byte(key)) + if bz == nil { + return + } + pk.checkType(key, ptr) + err := amino.UnmarshalJSON(bz, ptr) + if err != nil { + panic(err) + } +} + +func (pk ParamsKeeper) GetRaw(ctx sdk.Context, key string) []byte { + stor := ctx.Store(pk.key) + return stor.Get([]byte(key)) +} + +func (pk ParamsKeeper) Set(ctx sdk.Context, key string, value interface{}) { + pk.checkType(key, value) + stor := ctx.Store(pk.key) + bz, err := amino.MarshalJSON(value) + if err != nil { + panic(err) + } + stor.Set([]byte(key), bz) +} + +func (pk ParamsKeeper) Update(ctx sdk.Context, key string, value []byte) error { + attr, ok := pk.table.m[key] + if !ok { + panic(fmt.Sprintf("parameter %s not registered", key)) + } + + ty := attr.ty + dest := reflect.New(ty).Interface() + pk.GetIfExists(ctx, key, dest) + + if err := amino.UnmarshalJSON(value, dest); err != nil { + return err + } + + destValue := reflect.Indirect(reflect.ValueOf(dest)).Interface() + if err := pk.Validate(ctx, key, destValue); err != nil { + return err + } + + pk.Set(ctx, key, dest) + return nil +} + +func (pk ParamsKeeper) Validate(ctx sdk.Context, key string, value interface{}) error { + attr, ok := pk.table.m[key] + if !ok { + return fmt.Errorf("parameter %s not registered", key) + } + + if err := attr.vfn(value); err != nil { + return fmt.Errorf("invalid parameter value: %w", err) + } + + return nil +} + +func (pk ParamsKeeper) checkType(key string, value interface{}) { + attr, ok := pk.table.m[key] + if !ok { + panic(fmt.Sprintf("parameter %s is not registered", key)) + } + + ty := attr.ty + pty := reflect.TypeOf(value) + if pty.Kind() == reflect.Ptr { + pty = pty.Elem() + } + + if pty != ty { + panic("type mismatch with registered table") + } +} + +func (pk ParamsKeeper) HasKeyTable() bool { + return len(pk.table.m) > 0 +} + +func (pk ParamsKeeper) WithKeyTable(table KeyTable) ParamsKeeper { + if table.m == nil { + panic("WithKeyTable() called with nil KeyTable") + } + if len(pk.table.m) != 0 { + panic("WithKeyTable() called on already initialized Keeper") + } + + maps.Copy(pk.table.m, table.m) + return pk +} + +// XXX: GetAllKeys +// XXX: GetAllParams +// XXX: ViewKeeper +// XXX: ModuleKeeper diff --git a/tm2/pkg/sdk/params/keeper_test.go b/tm2/pkg/sdk/params/keeper_test.go new file mode 100644 index 00000000000..6fd1e6ae65b --- /dev/null +++ b/tm2/pkg/sdk/params/keeper_test.go @@ -0,0 +1,209 @@ +package params + +import ( + "reflect" + "testing" + + "github.com/gnolang/gno/tm2/pkg/amino" + "github.com/stretchr/testify/require" +) + +// XXX: transient test + +func TestKeeper(t *testing.T) { + kvs := []struct { + key string + param int64 + }{ + {"key1", 10}, + {"key2", 55}, + {"key3", 182}, + {"key4", 17582}, + {"key5", 2768554}, + {"key6", 1157279}, + {"key7", 9058701}, + } + + table := NewKeyTable( + NewParamSetPair("key1", int64(0), validateNoOp), + NewParamSetPair("key2", int64(0), validateNoOp), + NewParamSetPair("key3", int64(0), validateNoOp), + NewParamSetPair("key4", int64(0), validateNoOp), + NewParamSetPair("key5", int64(0), validateNoOp), + NewParamSetPair("key6", int64(0), validateNoOp), + NewParamSetPair("key7", int64(0), validateNoOp), + NewParamSetPair("extra1", bool(false), validateNoOp), + NewParamSetPair("extra2", string(""), validateNoOp), + ) + + env := setupTestEnv() + ctx, store, keeper := env.ctx, env.store, env.keeper + + require.False(t, keeper.HasKeyTable()) + keeper = keeper.WithKeyTable(table) + require.True(t, keeper.HasKeyTable()) + + // Set params + for i, kv := range kvs { + require.NotPanics(t, func() { keeper.Set(ctx, kv.key, kv.param) }, "keeper.Set panics, tc #%d", i) + } + + // Test keeper.Get + for i, kv := range kvs { + var param int64 + require.NotPanics(t, func() { keeper.Get(ctx, kv.key, ¶m) }, "keeper.Get panics, tc #%d", i) + require.Equal(t, kv.param, param, "stored param not equal, tc #%d", i) + } + + // Test keeper.GetRaw + for i, kv := range kvs { + var param int64 + bz := keeper.GetRaw(ctx, kv.key) + err := amino.UnmarshalJSON(bz, ¶m) + require.Nil(t, err, "err is not nil, tc #%d", i) + require.Equal(t, kv.param, param, "stored param not equal, tc #%d", i) + } + + // Test store.Get equals keeper.Get + for i, kv := range kvs { + var param int64 + bz := store.Get([]byte(kv.key)) + require.NotNil(t, bz, "KVStore.Get returns nil, tc #%d", i) + err := amino.UnmarshalJSON(bz, ¶m) + require.NoError(t, err, "UnmarshalJSON returns error, tc #%d", i) + require.Equal(t, kv.param, param, "stored param not equal, tc #%d", i) + } + + // Test invalid keeper.Get + for i, kv := range kvs { + var param bool + require.Panics(t, func() { keeper.Get(ctx, kv.key, ¶m) }, "invalid keeper.Get not panics, tc #%d", i) + } + + // Test invalid keeper.Set + for i, kv := range kvs { + require.Panics(t, func() { keeper.Set(ctx, kv.key, true) }, "invalid keeper.Set not panics, tc #%d", i) + } +} + +// adapted from TestKeeperSubspace from Cosmos SDK, but adapted to a subspace-less Keeper. +func TestKeeper_Subspace(t *testing.T) { + env := setupTestEnv() + ctx, store, keeper := env.ctx, env.store, env.keeper + // XXX: keeper = keeper.WithKeyTable(table) + + // cdc, ctx, key, _, keeper := testComponents() + + kvs := []struct { + key string + param interface{} + zero interface{} + ptr interface{} + }{ + {"string", "test", "", new(string)}, + {"bool", true, false, new(bool)}, + {"int16", int16(1), int16(0), new(int16)}, + {"int32", int32(1), int32(0), new(int32)}, + {"int64", int64(1), int64(0), new(int64)}, + {"uint16", uint16(1), uint16(0), new(uint16)}, + {"uint32", uint32(1), uint32(0), new(uint32)}, + {"uint64", uint64(1), uint64(0), new(uint64)}, + // XXX: {"int", math.NewInt(1), math.Int{}, new(math.Int)}, + // XXX: {"uint", math.NewUint(1), math.Uint{}, new(math.Uint)}, + // XXX: {"dec", math.LegacyNewDec(1), math.LegacyDec{}, new(math.LegacyDec)}, + {"struct", s{1}, s{0}, new(s)}, + } + + table := NewKeyTable( + NewParamSetPair("string", "", validateNoOp), + NewParamSetPair("bool", false, validateNoOp), + NewParamSetPair("int16", int16(0), validateNoOp), + NewParamSetPair("int32", int32(0), validateNoOp), + NewParamSetPair("int64", int64(0), validateNoOp), + NewParamSetPair("uint16", uint16(0), validateNoOp), + NewParamSetPair("uint32", uint32(0), validateNoOp), + NewParamSetPair("uint64", uint64(0), validateNoOp), + // XXX: NewParamSetPair("int", math.Int{}, validateNoOp), + // XXX: NewParamSetPair("uint", math.Uint{}, validateNoOp), + // XXX: NewParamSetPair("dec", math.LegacyDec{}, validateNoOp), + NewParamSetPair("struct", s{}, validateNoOp), + ) + keeper = keeper.WithKeyTable(table) + + // Test keeper.Set, keeper.Modified + for i, kv := range kvs { + // require.False(t, keeper.Modified(ctx, kv.key), "keeper.Modified returns true before setting, tc #%d", i) + require.NotPanics(t, func() { keeper.Set(ctx, kv.key, kv.param) }, "keeper.Set panics, tc #%d", i) + // require.True(t, keeper.Modified(ctx, kv.key), "keeper.Modified returns false after setting, tc #%d", i) + } + + // Test keeper.Get, keeper.GetIfExists + for i, kv := range kvs { + require.NotPanics(t, func() { keeper.GetIfExists(ctx, "invalid", kv.ptr) }, "keeper.GetIfExists panics when no value exists, tc #%d", i) + require.Equal(t, kv.zero, indirect(kv.ptr), "keeper.GetIfExists unmarshalls when no value exists, tc #%d", i) + require.Panics(t, func() { keeper.Get(ctx, "invalid", kv.ptr) }, "invalid keeper.Get not panics when no value exists, tc #%d", i) + require.Equal(t, kv.zero, indirect(kv.ptr), "invalid keeper.Get unmarshalls when no value exists, tc #%d", i) + + require.NotPanics(t, func() { keeper.GetIfExists(ctx, kv.key, kv.ptr) }, "keeper.GetIfExists panics, tc #%d", i) + require.Equal(t, kv.param, indirect(kv.ptr), "stored param not equal, tc #%d", i) + require.NotPanics(t, func() { keeper.Get(ctx, kv.key, kv.ptr) }, "keeper.Get panics, tc #%d", i) + require.Equal(t, kv.param, indirect(kv.ptr), "stored param not equal, tc #%d", i) + + require.Panics(t, func() { keeper.Get(ctx, "invalid", kv.ptr) }, "invalid keeper.Get not panics when no value exists, tc #%d", i) + require.Equal(t, kv.param, indirect(kv.ptr), "invalid keeper.Get unmarshalls when no value existt, tc #%d", i) + + require.Panics(t, func() { keeper.Get(ctx, kv.key, nil) }, "invalid keeper.Get not panics when the pointer is nil, tc #%d", i) + require.Panics(t, func() { keeper.Get(ctx, kv.key, new(invalid)) }, "invalid keeper.Get not panics when the pointer is different type, tc #%d", i) + } + + // Test store.Get equals keeper.Get + for i, kv := range kvs { + bz := store.Get([]byte(kv.key)) + require.NotNil(t, bz, "store.Get() returns nil, tc #%d", i) + err := amino.UnmarshalJSON(bz, kv.ptr) + require.NoError(t, err, "cdc.UnmarshalJSON() returns error, tc #%d", i) + require.Equal(t, kv.param, indirect(kv.ptr), "stored param not equal, tc #%d", i) + } +} + +func TestJSONUpdate(t *testing.T) { + env := setupTestEnv() + ctx, keeper := env.ctx, env.keeper + key := "key" + + space := keeper.WithKeyTable(NewKeyTable(NewParamSetPair(key, paramJSON{}, validateNoOp))) + + var param paramJSON + + err := space.Update(ctx, key, []byte(`{"param1": "10241024"}`)) + require.NoError(t, err) + space.Get(ctx, key, ¶m) + require.Equal(t, paramJSON{10241024, ""}, param) + + err = space.Update(ctx, key, []byte(`{"param2": "helloworld"}`)) + require.NoError(t, err) + space.Get(ctx, key, ¶m) + require.Equal(t, paramJSON{10241024, "helloworld"}, param) + + err = space.Update(ctx, key, []byte(`{"param1": "20482048"}`)) + require.NoError(t, err) + space.Get(ctx, key, ¶m) + require.Equal(t, paramJSON{20482048, "helloworld"}, param) + + err = space.Update(ctx, key, []byte(`{"param1": "40964096", "param2": "goodbyeworld"}`)) + require.NoError(t, err) + space.Get(ctx, key, ¶m) + require.Equal(t, paramJSON{40964096, "goodbyeworld"}, param) +} + +type ( + invalid struct{} + s struct{ I int } + paramJSON struct { + Param1 int64 `json:"param1,omitempty" yaml:"param1,omitempty"` + Param2 string `json:"param2,omitempty" yaml:"param2,omitempty"` + } +) + +func validateNoOp(_ interface{}) error { return nil } +func indirect(ptr interface{}) interface{} { return reflect.ValueOf(ptr).Elem().Interface() } diff --git a/tm2/pkg/sdk/params/paramset.go b/tm2/pkg/sdk/params/paramset.go new file mode 100644 index 00000000000..b401c56e69e --- /dev/null +++ b/tm2/pkg/sdk/params/paramset.go @@ -0,0 +1,25 @@ +package params + +// This file mirrors the original implementation from the Cosmos SDK. + +type ValueValidatorFn func(value interface{}) error + +// ParamSetPair is used for associating param key and field of param structs. +type ParamSetPair struct { + Key string + Value interface{} + ValidatorFn ValueValidatorFn +} + +// NewParamSetPair creates a new ParamSetPair instance. +func NewParamSetPair(key string, value interface{}, vfn ValueValidatorFn) ParamSetPair { + return ParamSetPair{key, value, vfn} +} + +// ParamSetPairs Slice of KeyFieldPair. +type ParamSetPairs []ParamSetPair + +// ParamSet defines an interface for structs containing parameters for a module. +type ParamSet interface { + ParamSetPairs() ParamSetPairs +} diff --git a/tm2/pkg/sdk/params/table.go b/tm2/pkg/sdk/params/table.go new file mode 100644 index 00000000000..1ce6a3b92db --- /dev/null +++ b/tm2/pkg/sdk/params/table.go @@ -0,0 +1,71 @@ +package params + +// This file closely mirrors the original implementation from the Cosmos SDK, with only minor modifications. + +import ( + "reflect" + "regexp" +) + +type attribute struct { + ty reflect.Type + vfn ValueValidatorFn +} + +// KeyTable subspaces appropriate type for each parameter key +type KeyTable struct { + m map[string]attribute +} + +func NewKeyTable(pairs ...ParamSetPair) KeyTable { + keyTable := KeyTable{ + m: make(map[string]attribute), + } + + for _, psp := range pairs { + keyTable = keyTable.RegisterType(psp) + } + + return keyTable +} + +// RegisterType registers a single ParamSetPair (key-type pair) in a KeyTable. +func (t KeyTable) RegisterType(psp ParamSetPair) KeyTable { + if len(psp.Key) == 0 { + panic("cannot register ParamSetPair with an parameter empty key") + } + if !isAlphaNumeric(psp.Key) { + panic("cannot register ParamSetPair with a non-alphanumeric parameter key") + } + if psp.ValidatorFn == nil { + panic("cannot register ParamSetPair without a value validation function") + } + + if _, ok := t.m[psp.Key]; ok { + panic("duplicate parameter key") + } + + rty := reflect.TypeOf(psp.Value) + + // indirect rty if it is a pointer + for rty.Kind() == reflect.Ptr { + rty = rty.Elem() + } + + t.m[psp.Key] = attribute{ + vfn: psp.ValidatorFn, + ty: rty, + } + + return t +} + +// RegisterParamSet registers multiple ParamSetPairs from a ParamSet in a KeyTable. +func (t KeyTable) RegisterParamSet(ps ParamSet) KeyTable { + for _, psp := range ps.ParamSetPairs() { + t = t.RegisterType(psp) + } + return t +} + +var isAlphaNumeric = regexp.MustCompile(`^[a-zA-Z0-9]+$`).MatchString diff --git a/tm2/pkg/sdk/params/test_common.go b/tm2/pkg/sdk/params/test_common.go new file mode 100644 index 00000000000..8243ee867de --- /dev/null +++ b/tm2/pkg/sdk/params/test_common.go @@ -0,0 +1,46 @@ +package params + +import ( + abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types" + bft "github.com/gnolang/gno/tm2/pkg/bft/types" + "github.com/gnolang/gno/tm2/pkg/db/memdb" + "github.com/gnolang/gno/tm2/pkg/log" + "github.com/gnolang/gno/tm2/pkg/sdk" + "github.com/gnolang/gno/tm2/pkg/store" + "github.com/gnolang/gno/tm2/pkg/store/iavl" +) + +type testEnv struct { + ctx sdk.Context + store store.Store + keeper ParamsKeeper +} + +func setupTestEnv() testEnv { + db := memdb.NewMemDB() + paramsCapKey := store.NewStoreKey("paramsCapKey") + ms := store.NewCommitMultiStore(db) + ms.MountStoreWithDB(paramsCapKey, iavl.StoreConstructor, db) + ms.LoadLatestVersion() + + prefix := "params_test" + keeper := NewParamsKeeper(paramsCapKey, prefix) + + ctx := sdk.NewContext(sdk.RunTxModeDeliver, ms, &bft.Header{Height: 1, ChainID: "test-chain-id"}, log.NewNoopLogger()) + // XXX: context key? + ctx = ctx.WithConsensusParams(&abci.ConsensusParams{ + Block: &abci.BlockParams{ + MaxTxBytes: 1024, + MaxDataBytes: 1024 * 100, + MaxBlockBytes: 1024 * 100, + MaxGas: 10 * 1000 * 1000, + TimeIotaMS: 10, + }, + Validator: &abci.ValidatorParams{ + PubKeyTypeURLs: []string{}, // XXX + }, + }) + + stor := ctx.Store(paramsCapKey) + return testEnv{ctx: ctx, store: stor, keeper: keeper} +} diff --git a/tm2/pkg/store/README.md b/tm2/pkg/store/README.md index abf5c26bc07..24ae0c805ac 100644 --- a/tm2/pkg/store/README.md +++ b/tm2/pkg/store/README.md @@ -116,15 +116,3 @@ type traceOperation struct { ``` `traceOperation.Metadata` is filled with `Store.context` when it is not nil. `TraceContext` is a `map[string]interface{}`. - -## Transient - -`transient.Store` is a base-layer `KVStore` which is automatically discarded at the end of the block. - -```go -type Store struct { - dbadapter.Store -} -``` - -`Store.Store` is a `dbadapter.Store` with a `memdb.NewMemDB()`. All `KVStore` methods are reused. When `Store.Commit()` is called, new `dbadapter.Store` is assigned, discarding previous reference and making it garbage collected. From 602245d2efde7af76fd9846c57a91838b2024f2d Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Wed, 9 Oct 2024 06:52:31 +0200 Subject: [PATCH 02/24] feat: inject ParamsKeeper in VMKeeper Signed-off-by: moul <94029+moul@users.noreply.github.com> --- gno.land/pkg/sdk/vm/builtins.go | 50 ++++++++++++++ gno.land/pkg/sdk/vm/common_test.go | 5 +- gno.land/pkg/sdk/vm/keeper.go | 11 ++- gno.land/pkg/sdk/vm/keeper_test.go | 54 +++++++++++++++ gnovm/stdlibs/generated.go | 104 +++++++++++++++++++++++++++++ gnovm/stdlibs/std/context.go | 1 + gnovm/stdlibs/std/params.gno | 28 ++++++++ gnovm/stdlibs/std/params.go | 62 +++++++++++++++++ misc/genstd/Makefile | 6 ++ tm2/pkg/sdk/params/doc.go | 2 + tm2/pkg/sdk/params/keeper.go | 4 ++ tm2/pkg/sdk/params/table.go | 5 +- 12 files changed, 328 insertions(+), 4 deletions(-) create mode 100644 gnovm/stdlibs/std/params.gno create mode 100644 gnovm/stdlibs/std/params.go create mode 100644 misc/genstd/Makefile diff --git a/gno.land/pkg/sdk/vm/builtins.go b/gno.land/pkg/sdk/vm/builtins.go index de58cd3e8ae..3355d6e088e 100644 --- a/gno.land/pkg/sdk/vm/builtins.go +++ b/gno.land/pkg/sdk/vm/builtins.go @@ -3,6 +3,7 @@ package vm import ( "github.com/gnolang/gno/tm2/pkg/crypto" "github.com/gnolang/gno/tm2/pkg/sdk" + "github.com/gnolang/gno/tm2/pkg/sdk/params" "github.com/gnolang/gno/tm2/pkg/std" ) @@ -55,3 +56,52 @@ func (bnk *SDKBanker) RemoveCoin(b32addr crypto.Bech32Address, denom string, amo panic(err) } } + +// ---------------------------------------- +// SDKParams + +type SDKParams struct { + vmk *VMKeeper + ctx sdk.Context +} + +func NewSDKParams(vmk *VMKeeper, ctx sdk.Context) *SDKParams { + return &SDKParams{ + vmk: vmk, + ctx: ctx, + } +} + +// SetXXX helpers: +// - dynamically register a new key with the corresponding type in the paramset table (only once). +// - set the value. + +func (prm *SDKParams) SetString(key, value string) { + if !prm.vmk.prmk.Has(prm.ctx, key) { + prm.vmk.prmk.RegisterType(params.NewParamSetPair(key, "", validateNoOp)) + } + prm.vmk.prmk.Set(prm.ctx, key, value) +} + +func (prm *SDKParams) SetBool(key string, value bool) { + if !prm.vmk.prmk.Has(prm.ctx, key) { + prm.vmk.prmk.RegisterType(params.NewParamSetPair(key, true, validateNoOp)) + } + prm.vmk.prmk.Set(prm.ctx, key, value) +} + +func (prm *SDKParams) SetInt64(key string, value int64) { + if !prm.vmk.prmk.Has(prm.ctx, key) { + prm.vmk.prmk.RegisterType(params.NewParamSetPair(key, int64(0), validateNoOp)) + } + prm.vmk.prmk.Set(prm.ctx, key, value) +} + +func (prm *SDKParams) SetUint64(key string, value uint64) { + if !prm.vmk.prmk.Has(prm.ctx, key) { + prm.vmk.prmk.RegisterType(params.NewParamSetPair(key, uint64(0), validateNoOp)) + } + prm.vmk.prmk.Set(prm.ctx, key, value) +} + +func validateNoOp(_ interface{}) error { return nil } diff --git a/gno.land/pkg/sdk/vm/common_test.go b/gno.land/pkg/sdk/vm/common_test.go index 43a8fe1fbec..e3acb278fb6 100644 --- a/gno.land/pkg/sdk/vm/common_test.go +++ b/gno.land/pkg/sdk/vm/common_test.go @@ -11,6 +11,7 @@ import ( "github.com/gnolang/gno/tm2/pkg/sdk" authm "github.com/gnolang/gno/tm2/pkg/sdk/auth" bankm "github.com/gnolang/gno/tm2/pkg/sdk/bank" + paramsm "github.com/gnolang/gno/tm2/pkg/sdk/params" "github.com/gnolang/gno/tm2/pkg/std" "github.com/gnolang/gno/tm2/pkg/store" "github.com/gnolang/gno/tm2/pkg/store/dbadapter" @@ -47,7 +48,9 @@ func _setupTestEnv(cacheStdlibs bool) testEnv { ctx := sdk.NewContext(sdk.RunTxModeDeliver, ms, &bft.Header{ChainID: "test-chain-id"}, log.NewNoopLogger()) acck := authm.NewAccountKeeper(iavlCapKey, std.ProtoBaseAccount) bank := bankm.NewBankKeeper(acck) - vmk := NewVMKeeper(baseCapKey, iavlCapKey, acck, bank, 100_000_000) + prmk := paramsm.NewParamsKeeper(iavlCapKey, "params") + maxCycles := int64(100_000_000) // XXX: use x/params for 100_000_000 + vmk := NewVMKeeper(baseCapKey, iavlCapKey, acck, bank, prmk, maxCycles) mcw := ms.MultiCacheWrap() vmk.Initialize(log.NewNoopLogger(), mcw) diff --git a/gno.land/pkg/sdk/vm/keeper.go b/gno.land/pkg/sdk/vm/keeper.go index 365473b3e7a..d6c6e4e63d1 100644 --- a/gno.land/pkg/sdk/vm/keeper.go +++ b/gno.land/pkg/sdk/vm/keeper.go @@ -23,6 +23,7 @@ import ( "github.com/gnolang/gno/tm2/pkg/sdk" "github.com/gnolang/gno/tm2/pkg/sdk/auth" "github.com/gnolang/gno/tm2/pkg/sdk/bank" + "github.com/gnolang/gno/tm2/pkg/sdk/params" "github.com/gnolang/gno/tm2/pkg/std" "github.com/gnolang/gno/tm2/pkg/store" "github.com/gnolang/gno/tm2/pkg/store/dbadapter" @@ -59,6 +60,7 @@ type VMKeeper struct { iavlKey store.StoreKey acck auth.AccountKeeper bank bank.BankKeeper + prmk params.ParamsKeeper // cached, the DeliverTx persistent state. gnoStore gno.Store @@ -72,14 +74,15 @@ func NewVMKeeper( iavlKey store.StoreKey, acck auth.AccountKeeper, bank bank.BankKeeper, + prmk params.ParamsKeeper, maxCycles int64, ) *VMKeeper { - // TODO: create an Options struct to avoid too many constructor parameters vmk := &VMKeeper{ baseKey: baseKey, iavlKey: iavlKey, acck: acck, bank: bank, + prmk: prmk, maxCycles: maxCycles, } return vmk @@ -262,6 +265,7 @@ func (vm *VMKeeper) checkNamespacePermission(ctx sdk.Context, creator crypto.Add OrigPkgAddr: pkgAddr.Bech32(), // XXX: should we remove the banker ? Banker: NewSDKBanker(vm, ctx), + Params: NewSDKParams(vm, ctx), EventLogger: ctx.EventLogger(), } @@ -363,6 +367,7 @@ func (vm *VMKeeper) AddPackage(ctx sdk.Context, msg MsgAddPackage) (err error) { OrigSendSpent: new(std.Coins), OrigPkgAddr: pkgAddr.Bech32(), Banker: NewSDKBanker(vm, ctx), + Params: NewSDKParams(vm, ctx), EventLogger: ctx.EventLogger(), } // Parse and run the files, construct *PV. @@ -464,6 +469,7 @@ func (vm *VMKeeper) Call(ctx sdk.Context, msg MsgCall) (res string, err error) { OrigSendSpent: new(std.Coins), OrigPkgAddr: pkgAddr.Bech32(), Banker: NewSDKBanker(vm, ctx), + Params: NewSDKParams(vm, ctx), EventLogger: ctx.EventLogger(), } // Construct machine and evaluate. @@ -563,6 +569,7 @@ func (vm *VMKeeper) Run(ctx sdk.Context, msg MsgRun) (res string, err error) { OrigSendSpent: new(std.Coins), OrigPkgAddr: pkgAddr.Bech32(), Banker: NewSDKBanker(vm, ctx), + Params: NewSDKParams(vm, ctx), EventLogger: ctx.EventLogger(), } // Parse and run the files, construct *PV. @@ -724,6 +731,7 @@ func (vm *VMKeeper) QueryEval(ctx sdk.Context, pkgPath string, expr string) (res // OrigSendSpent: nil, OrigPkgAddr: pkgAddr.Bech32(), Banker: NewSDKBanker(vm, ctx), // safe as long as ctx is a fork to be discarded. + Params: NewSDKParams(vm, ctx), EventLogger: ctx.EventLogger(), } m := gno.NewMachineWithOptions( @@ -791,6 +799,7 @@ func (vm *VMKeeper) QueryEvalString(ctx sdk.Context, pkgPath string, expr string // OrigSendSpent: nil, OrigPkgAddr: pkgAddr.Bech32(), Banker: NewSDKBanker(vm, ctx), // safe as long as ctx is a fork to be discarded. + Params: NewSDKParams(vm, ctx), EventLogger: ctx.EventLogger(), } m := gno.NewMachineWithOptions( diff --git a/gno.land/pkg/sdk/vm/keeper_test.go b/gno.land/pkg/sdk/vm/keeper_test.go index 9257da2ddaf..9f3c3e422bf 100644 --- a/gno.land/pkg/sdk/vm/keeper_test.go +++ b/gno.land/pkg/sdk/vm/keeper_test.go @@ -298,6 +298,60 @@ func Echo(msg string) string { assert.Error(t, err) } +// Using x/params from a realm. +func TestVMKeeperParams(t *testing.T) { + env := setupTestEnv() + ctx := env.vmk.MakeGnoTransactionStore(env.ctx) + + // Give "addr1" some gnots. + addr := crypto.AddressFromPreimage([]byte("addr1")) + acc := env.acck.NewAccountWithAddress(ctx, addr) + env.acck.SetAccount(ctx, acc) + env.bank.SetCoins(ctx, addr, std.MustParseCoins(coinsString)) + // env.prmk. + assert.True(t, env.bank.GetCoins(ctx, addr).IsEqual(std.MustParseCoins(coinsString))) + + // Create test package. + files := []*std.MemFile{ + {"init.gno", ` +package test + +import "std" + +func init() { + std.SetConfig("foo", "foo1") +} + +func Do() string { + std.SetConfig("bar", int64(1337)) + std.SetConfig("foo", "foo2") // override init + + return "XXX" // return std.GetConfig("gno.land/r/test.foo"), if we want to expose std.GetConfig, maybe as a std.TestGetConfig +}`}, + } + pkgPath := "gno.land/r/test" + msg1 := NewMsgAddPackage(addr, pkgPath, files) + err := env.vmk.AddPackage(ctx, msg1) + assert.NoError(t, err) + + // Run Echo function. + coins := std.MustParseCoins(ugnot.ValueString(9_000_000)) + msg2 := NewMsgCall(addr, coins, pkgPath, "Do", []string{}) + + res, err := env.vmk.Call(ctx, msg2) + assert.NoError(t, err) + _ = res + expected := fmt.Sprintf("(\"%s\" string)\n\n", "XXX") // XXX: return something more useful + assert.Equal(t, expected, res) + + var foo string + var bar int64 + env.vmk.prmk.Get(ctx, "gno.land/r/test.foo.string", &foo) + env.vmk.prmk.Get(ctx, "gno.land/r/test.bar.int64", &bar) + assert.Equal(t, "foo2", foo) + assert.Equal(t, int64(1337), bar) +} + // Assign admin as OrigCaller on deploying the package. func TestVMKeeperOrigCallerInit(t *testing.T) { env := setupTestEnv() diff --git a/gnovm/stdlibs/generated.go b/gnovm/stdlibs/generated.go index 4c460e220b7..e6eca53ffe3 100644 --- a/gnovm/stdlibs/generated.go +++ b/gnovm/stdlibs/generated.go @@ -721,6 +721,110 @@ var nativeFuncs = [...]NativeFunc{ )) }, }, + { + "std", + "setConfigString", + []gno.FieldTypeExpr{ + {Name: gno.N("p0"), Type: gno.X("string")}, + {Name: gno.N("p1"), Type: gno.X("string")}, + }, + []gno.FieldTypeExpr{}, + true, + func(m *gno.Machine) { + b := m.LastBlock() + var ( + p0 string + rp0 = reflect.ValueOf(&p0).Elem() + p1 string + rp1 = reflect.ValueOf(&p1).Elem() + ) + + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0) + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 1, "")).TV, rp1) + + libs_std.X_setConfigString( + m, + p0, p1) + }, + }, + { + "std", + "setConfigBool", + []gno.FieldTypeExpr{ + {Name: gno.N("p0"), Type: gno.X("string")}, + {Name: gno.N("p1"), Type: gno.X("bool")}, + }, + []gno.FieldTypeExpr{}, + true, + func(m *gno.Machine) { + b := m.LastBlock() + var ( + p0 string + rp0 = reflect.ValueOf(&p0).Elem() + p1 bool + rp1 = reflect.ValueOf(&p1).Elem() + ) + + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0) + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 1, "")).TV, rp1) + + libs_std.X_setConfigBool( + m, + p0, p1) + }, + }, + { + "std", + "setConfigInt64", + []gno.FieldTypeExpr{ + {Name: gno.N("p0"), Type: gno.X("string")}, + {Name: gno.N("p1"), Type: gno.X("int64")}, + }, + []gno.FieldTypeExpr{}, + true, + func(m *gno.Machine) { + b := m.LastBlock() + var ( + p0 string + rp0 = reflect.ValueOf(&p0).Elem() + p1 int64 + rp1 = reflect.ValueOf(&p1).Elem() + ) + + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0) + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 1, "")).TV, rp1) + + libs_std.X_setConfigInt64( + m, + p0, p1) + }, + }, + { + "std", + "setConfigUint64", + []gno.FieldTypeExpr{ + {Name: gno.N("p0"), Type: gno.X("string")}, + {Name: gno.N("p1"), Type: gno.X("uint64")}, + }, + []gno.FieldTypeExpr{}, + true, + func(m *gno.Machine) { + b := m.LastBlock() + var ( + p0 string + rp0 = reflect.ValueOf(&p0).Elem() + p1 uint64 + rp1 = reflect.ValueOf(&p1).Elem() + ) + + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0) + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 1, "")).TV, rp1) + + libs_std.X_setConfigUint64( + m, + p0, p1) + }, + }, { "strconv", "Itoa", diff --git a/gnovm/stdlibs/std/context.go b/gnovm/stdlibs/std/context.go index ff5c91a14eb..a0dafe5dc44 100644 --- a/gnovm/stdlibs/std/context.go +++ b/gnovm/stdlibs/std/context.go @@ -18,6 +18,7 @@ type ExecContext struct { OrigSend std.Coins OrigSendSpent *std.Coins // mutable Banker BankerInterface + Params ParamsInterface EventLogger *sdk.EventLogger } diff --git a/gnovm/stdlibs/std/params.gno b/gnovm/stdlibs/std/params.gno new file mode 100644 index 00000000000..a3dc47d4bd4 --- /dev/null +++ b/gnovm/stdlibs/std/params.gno @@ -0,0 +1,28 @@ +package std + +// These are native bindings to the banker's functions. +func setConfigString(key string, val string) +func setConfigBool(key string, val bool) +func setConfigInt64(key string, val int64) +func setConfigUint64(key string, val uint64) + +// XXX: add doc +func SetConfig(key string, val interface{}) { + switch v := val.(type) { + case string: + setConfigString(key, v) + case bool: + setConfigBool(key, v) + case int64: + setConfigInt64(key, v) + case uint64: + setConfigUint64(key, v) + default: + panic("unsupported type") + } +} + +// XXX: add doc +//func GetConfig(key string) string { +// return getConfig(key) +//} diff --git a/gnovm/stdlibs/std/params.go b/gnovm/stdlibs/std/params.go new file mode 100644 index 00000000000..008315bfd98 --- /dev/null +++ b/gnovm/stdlibs/std/params.go @@ -0,0 +1,62 @@ +package std + +import ( + "fmt" + "unicode" + + gno "github.com/gnolang/gno/gnovm/pkg/gnolang" +) + +// ParamsInterface is the interface through which Gno is capable of accessing +// the blockchain's params. +// +// The name is what it is to avoid a collision with Gno's Params, when +// transpiling. +type ParamsInterface interface { + SetString(key, val string) + SetBool(key string, val bool) + SetInt64(key string, val int64) + SetUint64(key string, val uint64) + // XXX: GetString(key string) (string, error)? + +} + +func X_setConfigString(m *gno.Machine, key, val string) { + pk := pkey(m, key, "string") + GetContext(m).Params.SetString(pk, val) +} + +func X_setConfigBool(m *gno.Machine, key string, val bool) { + pk := pkey(m, key, "bool") + GetContext(m).Params.SetBool(pk, val) +} + +func X_setConfigInt64(m *gno.Machine, key string, val int64) { + pk := pkey(m, key, "int64") + GetContext(m).Params.SetInt64(pk, val) +} + +func X_setConfigUint64(m *gno.Machine, key string, val uint64) { + pk := pkey(m, key, "uint64") + GetContext(m).Params.SetUint64(pk, val) +} + +func pkey(m *gno.Machine, key string, kind string) string { + // validate key. + if len(key) == 0 { + panic("empty param key") + } + first := rune(key[0]) + if !unicode.IsLetter(first) && first != '_' { + panic("invalid param key: " + key) + } + for _, char := range key[1:] { + if !unicode.IsLetter(char) && !unicode.IsDigit(char) && char != '_' { + panic("invalid param key: " + key) + } + } + + // decorate key with realm and type. + _, rlmPath := currentRealm(m) + return fmt.Sprintf("%s.%s.%s", rlmPath, key, kind) +} diff --git a/misc/genstd/Makefile b/misc/genstd/Makefile new file mode 100644 index 00000000000..2022a6cc2b4 --- /dev/null +++ b/misc/genstd/Makefile @@ -0,0 +1,6 @@ +run: + cd ../../gnovm/stdlibs && go run ../../misc/genstd + cd ../../gnovm/tests/stdlibs && go run ../../../misc/genstd + +test: + go test -v . diff --git a/tm2/pkg/sdk/params/doc.go b/tm2/pkg/sdk/params/doc.go index bf7449adb49..a433b5eb115 100644 --- a/tm2/pkg/sdk/params/doc.go +++ b/tm2/pkg/sdk/params/doc.go @@ -10,4 +10,6 @@ // transient store and .Modified helper have also been removed but can be // implemented later if needed. Keys are represented as strings instead of // []byte. +// +// XXX: removes isAlphaNum validation for keys. package params diff --git a/tm2/pkg/sdk/params/keeper.go b/tm2/pkg/sdk/params/keeper.go index da1283bd5cf..e24e216bafe 100644 --- a/tm2/pkg/sdk/params/keeper.go +++ b/tm2/pkg/sdk/params/keeper.go @@ -145,6 +145,10 @@ func (pk ParamsKeeper) WithKeyTable(table KeyTable) ParamsKeeper { return pk } +func (pk ParamsKeeper) RegisterType(psp ParamSetPair) { + pk.table.RegisterType(psp) +} + // XXX: GetAllKeys // XXX: GetAllParams // XXX: ViewKeeper diff --git a/tm2/pkg/sdk/params/table.go b/tm2/pkg/sdk/params/table.go index 1ce6a3b92db..34747f0e455 100644 --- a/tm2/pkg/sdk/params/table.go +++ b/tm2/pkg/sdk/params/table.go @@ -34,9 +34,10 @@ func (t KeyTable) RegisterType(psp ParamSetPair) KeyTable { if len(psp.Key) == 0 { panic("cannot register ParamSetPair with an parameter empty key") } - if !isAlphaNumeric(psp.Key) { + // XXX: sanitize more? + /*if !isAlphaNumeric(psp.Key) { panic("cannot register ParamSetPair with a non-alphanumeric parameter key") - } + }*/ if psp.ValidatorFn == nil { panic("cannot register ParamSetPair without a value validation function") } From 783a044e7505e7fde074bb7a1560b69107132228 Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Fri, 11 Oct 2024 01:22:35 +0200 Subject: [PATCH 03/24] feat: configure params in gno.land Signed-off-by: moul <94029+moul@users.noreply.github.com> --- gno.land/cmd/gnoland/testdata/params.txtar | 70 ++++++++++++++++++++++ gno.land/pkg/gnoland/app.go | 16 +++-- gno.land/pkg/sdk/vm/builtins.go | 13 ++-- gnovm/stdlibs/std/params.go | 1 - tm2/pkg/sdk/params/handler.go | 11 ++-- tm2/pkg/sdk/params/keeper.go | 13 ++++ tm2/pkg/sdk/params/table.go | 7 ++- 7 files changed, 113 insertions(+), 18 deletions(-) create mode 100644 gno.land/cmd/gnoland/testdata/params.txtar diff --git a/gno.land/cmd/gnoland/testdata/params.txtar b/gno.land/cmd/gnoland/testdata/params.txtar new file mode 100644 index 00000000000..1c19bc854e1 --- /dev/null +++ b/gno.land/cmd/gnoland/testdata/params.txtar @@ -0,0 +1,70 @@ +# test for https://github.com/gnolang/gno/pull/2920 + +gnoland start + +# query before adding the package +gnokey query params/vm/gno.land/r/sys/setter.foo.string +stdout 'data: $' +gnokey query params/vm/gno.land/r/sys/setter.bar.bool +stdout 'data: $' +gnokey query params/vm/gno.land/r/sys/setter.baz.int64 +stdout 'data: $' + +gnokey maketx addpkg -pkgdir $WORK/setter -pkgpath gno.land/r/sys/setter -gas-fee 1000000ugnot -gas-wanted 100000000 -broadcast -chainid=tendermint_test test1 + +# query after adding the package, but before setting values +gnokey query params/vm/gno.land/r/sys/setter.foo.string +stdout 'data: $' +gnokey query params/vm/gno.land/r/sys/setter.bar.bool +stdout 'data: $' +gnokey query params/vm/gno.land/r/sys/setter.baz.int64 +stdout 'data: $' + + +# set foo (string) +gnokey maketx call -pkgpath gno.land/r/sys/setter -func SetFoo -args foo1 -gas-fee 1000000ugnot -gas-wanted 10000000 -broadcast -chainid=tendermint_test test1 +gnokey query params/vm/gno.land/r/sys/setter.foo.string +stdout 'data: "foo1"' + +# override foo +gnokey maketx call -pkgpath gno.land/r/sys/setter -func SetFoo -args foo2 -gas-fee 1000000ugnot -gas-wanted 10000000 -broadcast -chainid=tendermint_test test1 +gnokey query params/vm/gno.land/r/sys/setter.foo.string +stdout 'data: "foo2"' + + +# set bar (bool) +gnokey maketx call -pkgpath gno.land/r/sys/setter -func SetBar -args true -gas-fee 1000000ugnot -gas-wanted 10000000 -broadcast -chainid=tendermint_test test1 +gnokey query params/vm/gno.land/r/sys/setter.bar.bool +stdout 'data: true' + +# override bar +gnokey maketx call -pkgpath gno.land/r/sys/setter -func SetBar -args false -gas-fee 1000000ugnot -gas-wanted 10000000 -broadcast -chainid=tendermint_test test1 +gnokey query params/vm/gno.land/r/sys/setter.bar.bool +stdout 'data: false' + + +# set baz (bool) +gnokey maketx call -pkgpath gno.land/r/sys/setter -func SetBaz -args 1337 -gas-fee 1000000ugnot -gas-wanted 10000000 -broadcast -chainid=tendermint_test test1 +gnokey query params/vm/gno.land/r/sys/setter.baz.int64 +stdout 'data: "1337"' + +# override baz +gnokey maketx call -pkgpath gno.land/r/sys/setter -func SetBaz -args 31337 -gas-fee 1000000ugnot -gas-wanted 10000000 -broadcast -chainid=tendermint_test test1 +gnokey query params/vm/gno.land/r/sys/setter.baz.int64 +stdout 'data: "31337"' + + + + +# XXX: create a non-sys package that should fail? + +-- setter/setter.gno -- +package setter + +import ( + "std" +) + +func SetFoo(newFoo string) { std.SetConfig("foo", newFoo) } +func SetBar(newBar bool) { std.SetConfig("bar", newBar) } +func SetBaz(newBaz int64) { std.SetConfig("baz", newBaz) } diff --git a/gno.land/pkg/gnoland/app.go b/gno.land/pkg/gnoland/app.go index 2380658c6e9..9caf2f5abd6 100644 --- a/gno.land/pkg/gnoland/app.go +++ b/gno.land/pkg/gnoland/app.go @@ -19,6 +19,7 @@ import ( "github.com/gnolang/gno/tm2/pkg/sdk" "github.com/gnolang/gno/tm2/pkg/sdk/auth" "github.com/gnolang/gno/tm2/pkg/sdk/bank" + "github.com/gnolang/gno/tm2/pkg/sdk/params" "github.com/gnolang/gno/tm2/pkg/std" "github.com/gnolang/gno/tm2/pkg/store" "github.com/gnolang/gno/tm2/pkg/store/dbadapter" @@ -88,12 +89,13 @@ func NewAppWithOptions(cfg *AppOptions) (abci.Application, error) { // Construct keepers. acctKpr := auth.NewAccountKeeper(mainKey, ProtoGnoAccount) bankKpr := bank.NewBankKeeper(acctKpr) - vmk := vm.NewVMKeeper(baseKey, mainKey, acctKpr, bankKpr, cfg.MaxCycles) + paramsKpr := params.NewParamsKeeper(mainKey, "vm") + vmk := vm.NewVMKeeper(baseKey, mainKey, acctKpr, bankKpr, paramsKpr, cfg.MaxCycles) // Set InitChainer icc := cfg.InitChainerConfig icc.baseApp = baseApp - icc.acctKpr, icc.bankKpr, icc.vmKpr = acctKpr, bankKpr, vmk + icc.acctKpr, icc.bankKpr, icc.vmKpr, icc.paramsKpr = acctKpr, bankKpr, vmk, paramsKpr baseApp.SetInitChainer(icc.InitChainer) // Set AnteHandler @@ -148,6 +150,7 @@ func NewAppWithOptions(cfg *AppOptions) (abci.Application, error) { // Set a handler Route. baseApp.Router().AddRoute("auth", auth.NewHandler(acctKpr)) baseApp.Router().AddRoute("bank", bank.NewHandler(bankKpr)) + baseApp.Router().AddRoute("params", params.NewHandler(paramsKpr)) baseApp.Router().AddRoute("vm", vm.NewHandler(vmk)) // Load latest version. @@ -225,10 +228,11 @@ type InitChainerConfig struct { // These fields are passed directly by NewAppWithOptions, and should not be // configurable by end-users. - baseApp *sdk.BaseApp - vmKpr vm.VMKeeperI - acctKpr auth.AccountKeeperI - bankKpr bank.BankKeeperI + baseApp *sdk.BaseApp + vmKpr vm.VMKeeperI + acctKpr auth.AccountKeeperI + bankKpr bank.BankKeeperI + paramsKpr params.ParamsKeeperI } // InitChainer is the function that can be used as a [sdk.InitChainer]. diff --git a/gno.land/pkg/sdk/vm/builtins.go b/gno.land/pkg/sdk/vm/builtins.go index 3355d6e088e..b679e5ebf2a 100644 --- a/gno.land/pkg/sdk/vm/builtins.go +++ b/gno.land/pkg/sdk/vm/builtins.go @@ -77,28 +77,33 @@ func NewSDKParams(vmk *VMKeeper, ctx sdk.Context) *SDKParams { // - set the value. func (prm *SDKParams) SetString(key, value string) { - if !prm.vmk.prmk.Has(prm.ctx, key) { + // if !prm.vmk.prmk.Has(prm.ctx, key) { + // XXX: bad workaround, maybe we should have a dedicated "dynamic keeper" allowing to create keys on the go? + if !prm.vmk.prmk.HasTypeKey(key) { prm.vmk.prmk.RegisterType(params.NewParamSetPair(key, "", validateNoOp)) } prm.vmk.prmk.Set(prm.ctx, key, value) } func (prm *SDKParams) SetBool(key string, value bool) { - if !prm.vmk.prmk.Has(prm.ctx, key) { + // if !prm.vmk.prmk.Has(prm.ctx, key) { + if !prm.vmk.prmk.HasTypeKey(key) { prm.vmk.prmk.RegisterType(params.NewParamSetPair(key, true, validateNoOp)) } prm.vmk.prmk.Set(prm.ctx, key, value) } func (prm *SDKParams) SetInt64(key string, value int64) { - if !prm.vmk.prmk.Has(prm.ctx, key) { + // if !prm.vmk.prmk.Has(prm.ctx, key) { + if !prm.vmk.prmk.HasTypeKey(key) { prm.vmk.prmk.RegisterType(params.NewParamSetPair(key, int64(0), validateNoOp)) } prm.vmk.prmk.Set(prm.ctx, key, value) } func (prm *SDKParams) SetUint64(key string, value uint64) { - if !prm.vmk.prmk.Has(prm.ctx, key) { + // if !prm.vmk.prmk.Has(prm.ctx, key) { + if !prm.vmk.prmk.HasTypeKey(key) { prm.vmk.prmk.RegisterType(params.NewParamSetPair(key, uint64(0), validateNoOp)) } prm.vmk.prmk.Set(prm.ctx, key, value) diff --git a/gnovm/stdlibs/std/params.go b/gnovm/stdlibs/std/params.go index 008315bfd98..70ecbe6ba1d 100644 --- a/gnovm/stdlibs/std/params.go +++ b/gnovm/stdlibs/std/params.go @@ -18,7 +18,6 @@ type ParamsInterface interface { SetInt64(key string, val int64) SetUint64(key string, val uint64) // XXX: GetString(key string) (string, error)? - } func X_setConfigString(m *gno.Machine, key, val string) { diff --git a/tm2/pkg/sdk/params/handler.go b/tm2/pkg/sdk/params/handler.go index 22b6961bd47..907482db420 100644 --- a/tm2/pkg/sdk/params/handler.go +++ b/tm2/pkg/sdk/params/handler.go @@ -27,11 +27,9 @@ func (bh paramsHandler) Process(ctx sdk.Context, msg std.Msg) sdk.Result { //---------------------------------------- // Query -const QueryParams = "params" - func (bh paramsHandler) Query(ctx sdk.Context, req abci.RequestQuery) (res abci.ResponseQuery) { switch secondPart(req.Path) { - case QueryParams: + case bh.params.prefix: return bh.queryParam(ctx, req) default: res = sdk.ABCIResponseQueryFromError( @@ -49,10 +47,11 @@ func (bh paramsHandler) queryParam(ctx sdk.Context, req abci.RequestQuery) (res std.ErrUnknownRequest("param key is empty")) } - // XXX: validate + // XXX: validate? - panic("not implemented") + val := bh.params.GetRaw(ctx, key) + res.Data = val return } @@ -79,5 +78,5 @@ func thirdPartWithSlashes(path string) string { if secondSlash == -1 { return "" // Return original if less than two slashes } - return path[strings.Index(path, "/")+secondSlash+1:] + return path[strings.Index(path, "/")+secondSlash+2:] } diff --git a/tm2/pkg/sdk/params/keeper.go b/tm2/pkg/sdk/params/keeper.go index e24e216bafe..0f7c14b5f47 100644 --- a/tm2/pkg/sdk/params/keeper.go +++ b/tm2/pkg/sdk/params/keeper.go @@ -11,6 +11,13 @@ import ( "github.com/gnolang/gno/tm2/pkg/store" ) +type ParamsKeeperI interface { + Get(ctx sdk.Context, key string, ptr interface{}) + Set(ctx sdk.Context, key string, value interface{}) +} + +var _ ParamsKeeperI = ParamsKeeper{} + // global paramstore Keeper. type ParamsKeeper struct { key store.StoreKey @@ -145,10 +152,16 @@ func (pk ParamsKeeper) WithKeyTable(table KeyTable) ParamsKeeper { return pk } +// XXX: added, should we remove? func (pk ParamsKeeper) RegisterType(psp ParamSetPair) { pk.table.RegisterType(psp) } +// XXX: added, should we remove? +func (pk ParamsKeeper) HasTypeKey(key string) bool { + return pk.table.HasKey(key) +} + // XXX: GetAllKeys // XXX: GetAllParams // XXX: ViewKeeper diff --git a/tm2/pkg/sdk/params/table.go b/tm2/pkg/sdk/params/table.go index 34747f0e455..dae3b0f62c6 100644 --- a/tm2/pkg/sdk/params/table.go +++ b/tm2/pkg/sdk/params/table.go @@ -43,7 +43,7 @@ func (t KeyTable) RegisterType(psp ParamSetPair) KeyTable { } if _, ok := t.m[psp.Key]; ok { - panic("duplicate parameter key") + panic("duplicate parameter key: " + psp.Key) } rty := reflect.TypeOf(psp.Value) @@ -69,4 +69,9 @@ func (t KeyTable) RegisterParamSet(ps ParamSet) KeyTable { return t } +func (t KeyTable) HasKey(key string) bool { + _, ok := t.m[key] + return ok +} + var isAlphaNumeric = regexp.MustCompile(`^[a-zA-Z0-9]+$`).MatchString From 89e046ce8534fba873d22b5398ad7eb3dc9b128f Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Mon, 21 Oct 2024 09:14:10 -0500 Subject: [PATCH 04/24] chore: remove unused param Signed-off-by: moul <94029+moul@users.noreply.github.com> --- gno.land/cmd/gnoland/start.go | 8 -------- 1 file changed, 8 deletions(-) diff --git a/gno.land/cmd/gnoland/start.go b/gno.land/cmd/gnoland/start.go index 21f0cb4b1a6..d871cb65aa1 100644 --- a/gno.land/cmd/gnoland/start.go +++ b/gno.land/cmd/gnoland/start.go @@ -51,7 +51,6 @@ type startCfg struct { genesisFile string chainID string dataDir string - genesisMaxVMCycles int64 config string lazyInit bool @@ -137,13 +136,6 @@ func (c *startCfg) RegisterFlags(fs *flag.FlagSet) { "replacement for '%%REMOTE%%' in genesis", ) - fs.Int64Var( - &c.genesisMaxVMCycles, - "genesis-max-vm-cycles", - 100_000_000, - "set maximum allowed vm cycles per operation. Zero means no limit.", - ) - fs.StringVar( &c.config, flagConfigFlag, From 5d1e36a59db9730a41cc2abbb934a77a2d26e501 Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Mon, 21 Oct 2024 09:20:35 -0500 Subject: [PATCH 05/24] chore: disable --flag-config-path Signed-off-by: moul <94029+moul@users.noreply.github.com> --- gno.land/cmd/gnoland/root.go | 10 +--------- gno.land/cmd/gnoland/start.go | 8 -------- 2 files changed, 1 insertion(+), 17 deletions(-) diff --git a/gno.land/cmd/gnoland/root.go b/gno.land/cmd/gnoland/root.go index 8df716b1fed..b40a1160b0b 100644 --- a/gno.land/cmd/gnoland/root.go +++ b/gno.land/cmd/gnoland/root.go @@ -5,12 +5,8 @@ import ( "os" "github.com/gnolang/gno/tm2/pkg/commands" - "github.com/peterbourgon/ff/v3" - "github.com/peterbourgon/ff/v3/fftoml" ) -const flagConfigFlag = "flag-config-path" - func main() { cmd := newRootCmd(commands.NewDefaultIO()) @@ -21,11 +17,7 @@ func newRootCmd(io commands.IO) *commands.Command { cmd := commands.NewCommand( commands.Metadata{ ShortUsage: " [flags] [...]", - ShortHelp: "starts the gnoland blockchain node", - Options: []ff.Option{ - ff.WithConfigFileFlag(flagConfigFlag), - ff.WithConfigFileParser(fftoml.Parser), - }, + ShortHelp: "manages the gnoland blockchain node", }, commands.NewEmptyConfig(), commands.HelpExec, diff --git a/gno.land/cmd/gnoland/start.go b/gno.land/cmd/gnoland/start.go index 21f0cb4b1a6..689c102722b 100644 --- a/gno.land/cmd/gnoland/start.go +++ b/gno.land/cmd/gnoland/start.go @@ -52,7 +52,6 @@ type startCfg struct { chainID string dataDir string genesisMaxVMCycles int64 - config string lazyInit bool logLevel string @@ -144,13 +143,6 @@ func (c *startCfg) RegisterFlags(fs *flag.FlagSet) { "set maximum allowed vm cycles per operation. Zero means no limit.", ) - fs.StringVar( - &c.config, - flagConfigFlag, - "", - "the flag config file (optional)", - ) - fs.StringVar( &c.logLevel, "log-level", From a39d1721739cddd4f4a470f55d92a7989188ff0f Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Mon, 21 Oct 2024 11:08:25 -0500 Subject: [PATCH 06/24] chore: fixup Signed-off-by: moul <94029+moul@users.noreply.github.com> --- gno.land/pkg/sdk/vm/gas_test.go | 2 +- gno.land/pkg/sdk/vm/keeper.go | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/gno.land/pkg/sdk/vm/gas_test.go b/gno.land/pkg/sdk/vm/gas_test.go index 4171b1cdbc3..53809a7f223 100644 --- a/gno.land/pkg/sdk/vm/gas_test.go +++ b/gno.land/pkg/sdk/vm/gas_test.go @@ -74,7 +74,7 @@ func TestAddPkgDeliverTx(t *testing.T) { assert.True(t, res.IsOK()) // NOTE: let's try to keep this bellow 100_000 :) - assert.Equal(t, int64(92825), gasDeliver) + assert.Equal(t, int64(93825), gasDeliver) } // Enough gas for a failed transaction. diff --git a/gno.land/pkg/sdk/vm/keeper.go b/gno.land/pkg/sdk/vm/keeper.go index d6c6e4e63d1..3118862a945 100644 --- a/gno.land/pkg/sdk/vm/keeper.go +++ b/gno.land/pkg/sdk/vm/keeper.go @@ -229,9 +229,12 @@ func (vm *VMKeeper) getGnoTransactionStore(ctx sdk.Context) gno.TransactionStore // Namespace can be either a user or crypto address. var reNamespace = regexp.MustCompile(`^gno.land/(?:r|p)/([\.~_a-zA-Z0-9]+)`) +const sysUsersPkgParamKey = "vm/gno.land/r/sys/params.string" + // checkNamespacePermission check if the user as given has correct permssion to on the given pkg path func (vm *VMKeeper) checkNamespacePermission(ctx sdk.Context, creator crypto.Address, pkgPath string) error { - const sysUsersPkg = "gno.land/r/sys/users" + var sysUsersPkg = "gno.land/r/sys/users" + vm.prmk.GetIfExists(ctx, sysUsersPkgParamKey, &sysUsersPkg) store := vm.getGnoTransactionStore(ctx) From 0342b61fa79bf6116ad85205ee174720181774e8 Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Mon, 21 Oct 2024 11:18:07 -0500 Subject: [PATCH 07/24] chore: fixup Signed-off-by: moul <94029+moul@users.noreply.github.com> --- gno.land/pkg/sdk/vm/keeper.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gno.land/pkg/sdk/vm/keeper.go b/gno.land/pkg/sdk/vm/keeper.go index 3118862a945..616e755ed31 100644 --- a/gno.land/pkg/sdk/vm/keeper.go +++ b/gno.land/pkg/sdk/vm/keeper.go @@ -230,10 +230,11 @@ func (vm *VMKeeper) getGnoTransactionStore(ctx sdk.Context) gno.TransactionStore var reNamespace = regexp.MustCompile(`^gno.land/(?:r|p)/([\.~_a-zA-Z0-9]+)`) const sysUsersPkgParamKey = "vm/gno.land/r/sys/params.string" +const defaultSysUsersPkg = "gno.land/r/sys/users" // checkNamespacePermission check if the user as given has correct permssion to on the given pkg path func (vm *VMKeeper) checkNamespacePermission(ctx sdk.Context, creator crypto.Address, pkgPath string) error { - var sysUsersPkg = "gno.land/r/sys/users" + sysUsersPkg := defaultSysUsersPkg vm.prmk.GetIfExists(ctx, sysUsersPkgParamKey, &sysUsersPkg) store := vm.getGnoTransactionStore(ctx) From dd8ca08b58067880c9000469ecf59a7f80bc587b Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Mon, 21 Oct 2024 12:25:24 -0500 Subject: [PATCH 08/24] chore: remove vmkeeper.maxcycles Signed-off-by: moul <94029+moul@users.noreply.github.com> --- gno.land/pkg/gnoland/app.go | 3 +- gno.land/pkg/gnoland/node_inmemory.go | 1 - gno.land/pkg/gnoland/vals.go | 61 --------------- gno.land/pkg/sdk/vm/keeper.go | 103 ++++++++++++-------------- 4 files changed, 47 insertions(+), 121 deletions(-) delete mode 100644 gno.land/pkg/gnoland/vals.go diff --git a/gno.land/pkg/gnoland/app.go b/gno.land/pkg/gnoland/app.go index 2380658c6e9..ca746dbe386 100644 --- a/gno.land/pkg/gnoland/app.go +++ b/gno.land/pkg/gnoland/app.go @@ -34,7 +34,6 @@ type AppOptions struct { DB dbm.DB // required Logger *slog.Logger // required EventSwitch events.EventSwitch // required - MaxCycles int64 // hard limit for cycles in GnoVM InitChainerConfig // options related to InitChainer } @@ -88,7 +87,7 @@ func NewAppWithOptions(cfg *AppOptions) (abci.Application, error) { // Construct keepers. acctKpr := auth.NewAccountKeeper(mainKey, ProtoGnoAccount) bankKpr := bank.NewBankKeeper(acctKpr) - vmk := vm.NewVMKeeper(baseKey, mainKey, acctKpr, bankKpr, cfg.MaxCycles) + vmk := vm.NewVMKeeper(baseKey, mainKey, acctKpr, bankKpr) // Set InitChainer icc := cfg.InitChainerConfig diff --git a/gno.land/pkg/gnoland/node_inmemory.go b/gno.land/pkg/gnoland/node_inmemory.go index d168c955607..5352f810a94 100644 --- a/gno.land/pkg/gnoland/node_inmemory.go +++ b/gno.land/pkg/gnoland/node_inmemory.go @@ -106,7 +106,6 @@ func NewInMemoryNode(logger *slog.Logger, cfg *InMemoryNodeConfig) (*node.Node, // Initialize the application with the provided options gnoApp, err := NewAppWithOptions(&AppOptions{ Logger: logger, - MaxCycles: cfg.GenesisMaxVMCycles, DB: cfg.DB, EventSwitch: evsw, InitChainerConfig: cfg.InitChainerConfig, diff --git a/gno.land/pkg/gnoland/vals.go b/gno.land/pkg/gnoland/vals.go deleted file mode 100644 index 1843dff3984..00000000000 --- a/gno.land/pkg/gnoland/vals.go +++ /dev/null @@ -1,61 +0,0 @@ -package gnoland - -import ( - "regexp" - - gnovm "github.com/gnolang/gno/gnovm/stdlibs/std" - "github.com/gnolang/gno/tm2/pkg/bft/types" - "github.com/gnolang/gno/tm2/pkg/events" -) - -const ( - valRealm = "gno.land/r/sys/validators" - valChangesFn = "GetChanges" - - validatorAddedEvent = "ValidatorAdded" - validatorRemovedEvent = "ValidatorRemoved" -) - -// XXX: replace with amino-based clean approach -var valRegexp = regexp.MustCompile(`{\("([^"]*)"\s[^)]+\),\("((?:[^"]|\\")*)"\s[^)]+\),\((\d+)\s[^)]+\)}`) - -// validatorUpdate is a type being used for "notifying" -// that a validator change happened on-chain. The events from `r/sys/validators` -// do not pass data related to validator add / remove instances (who, what, how) -type validatorUpdate struct{} - -// validatorEventFilter filters the given event to determine if it -// is tied to a validator update -func validatorEventFilter(event events.Event) []validatorUpdate { - // Make sure the event is a new TX event - txResult, ok := event.(types.EventTx) - if !ok { - return nil - } - - // Make sure an add / remove event happened - for _, ev := range txResult.Result.Response.Events { - // Make sure the event is a GnoVM event - gnoEv, ok := ev.(gnovm.GnoEvent) - if !ok { - continue - } - - // Make sure the event is from `r/sys/validators` - if gnoEv.PkgPath != valRealm { - continue - } - - // Make sure the event is either an add / remove - switch gnoEv.Type { - case validatorAddedEvent, validatorRemovedEvent: - // We don't pass data around with the events, but a single - // notification is enough to "trigger" a VM scrape - return []validatorUpdate{{}} - default: - continue - } - } - - return nil -} diff --git a/gno.land/pkg/sdk/vm/keeper.go b/gno.land/pkg/sdk/vm/keeper.go index 365473b3e7a..f069cce3723 100644 --- a/gno.land/pkg/sdk/vm/keeper.go +++ b/gno.land/pkg/sdk/vm/keeper.go @@ -62,8 +62,6 @@ type VMKeeper struct { // cached, the DeliverTx persistent state. gnoStore gno.Store - - maxCycles int64 // max allowed cylces on VM executions } // NewVMKeeper returns a new VMKeeper. @@ -72,15 +70,13 @@ func NewVMKeeper( iavlKey store.StoreKey, acck auth.AccountKeeper, bank bank.BankKeeper, - maxCycles int64, ) *VMKeeper { // TODO: create an Options struct to avoid too many constructor parameters vmk := &VMKeeper{ - baseKey: baseKey, - iavlKey: iavlKey, - acck: acck, - bank: bank, - maxCycles: maxCycles, + baseKey: baseKey, + iavlKey: iavlKey, + acck: acck, + bank: bank, } return vmk } @@ -267,13 +263,12 @@ func (vm *VMKeeper) checkNamespacePermission(ctx sdk.Context, creator crypto.Add m := gno.NewMachineWithOptions( gno.MachineOptions{ - PkgPath: "", - Output: os.Stdout, // XXX - Store: store, - Context: msgCtx, - Alloc: store.GetAllocator(), - MaxCycles: vm.maxCycles, - GasMeter: ctx.GasMeter(), + PkgPath: "", + Output: os.Stdout, // XXX + Store: store, + Context: msgCtx, + Alloc: store.GetAllocator(), + GasMeter: ctx.GasMeter(), }) defer m.Release() @@ -368,13 +363,12 @@ func (vm *VMKeeper) AddPackage(ctx sdk.Context, msg MsgAddPackage) (err error) { // Parse and run the files, construct *PV. m2 := gno.NewMachineWithOptions( gno.MachineOptions{ - PkgPath: "", - Output: os.Stdout, // XXX - Store: gnostore, - Alloc: gnostore.GetAllocator(), - Context: msgCtx, - MaxCycles: vm.maxCycles, - GasMeter: ctx.GasMeter(), + PkgPath: "", + Output: os.Stdout, // XXX + Store: gnostore, + Alloc: gnostore.GetAllocator(), + Context: msgCtx, + GasMeter: ctx.GasMeter(), }) defer m2.Release() defer func() { @@ -469,13 +463,12 @@ func (vm *VMKeeper) Call(ctx sdk.Context, msg MsgCall) (res string, err error) { // Construct machine and evaluate. m := gno.NewMachineWithOptions( gno.MachineOptions{ - PkgPath: "", - Output: os.Stdout, // XXX - Store: gnostore, - Context: msgCtx, - Alloc: gnostore.GetAllocator(), - MaxCycles: vm.maxCycles, - GasMeter: ctx.GasMeter(), + PkgPath: "", + Output: os.Stdout, // XXX + Store: gnostore, + Context: msgCtx, + Alloc: gnostore.GetAllocator(), + GasMeter: ctx.GasMeter(), }) defer m.Release() m.SetActivePackage(mpv) @@ -569,13 +562,12 @@ func (vm *VMKeeper) Run(ctx sdk.Context, msg MsgRun) (res string, err error) { buf := new(bytes.Buffer) m := gno.NewMachineWithOptions( gno.MachineOptions{ - PkgPath: "", - Output: buf, - Store: gnostore, - Alloc: gnostore.GetAllocator(), - Context: msgCtx, - MaxCycles: vm.maxCycles, - GasMeter: ctx.GasMeter(), + PkgPath: "", + Output: buf, + Store: gnostore, + Alloc: gnostore.GetAllocator(), + Context: msgCtx, + GasMeter: ctx.GasMeter(), }) // XXX MsgRun does not have pkgPath. How do we find it on chain? defer m.Release() @@ -596,13 +588,12 @@ func (vm *VMKeeper) Run(ctx sdk.Context, msg MsgRun) (res string, err error) { m2 := gno.NewMachineWithOptions( gno.MachineOptions{ - PkgPath: "", - Output: buf, - Store: gnostore, - Alloc: gnostore.GetAllocator(), - Context: msgCtx, - MaxCycles: vm.maxCycles, - GasMeter: ctx.GasMeter(), + PkgPath: "", + Output: buf, + Store: gnostore, + Alloc: gnostore.GetAllocator(), + Context: msgCtx, + GasMeter: ctx.GasMeter(), }) defer m2.Release() m2.SetActivePackage(pv) @@ -728,13 +719,12 @@ func (vm *VMKeeper) QueryEval(ctx sdk.Context, pkgPath string, expr string) (res } m := gno.NewMachineWithOptions( gno.MachineOptions{ - PkgPath: pkgPath, - Output: os.Stdout, // XXX - Store: gnostore, - Context: msgCtx, - Alloc: alloc, - MaxCycles: vm.maxCycles, - GasMeter: ctx.GasMeter(), + PkgPath: pkgPath, + Output: os.Stdout, // XXX + Store: gnostore, + Context: msgCtx, + Alloc: alloc, + GasMeter: ctx.GasMeter(), }) defer m.Release() defer func() { @@ -795,13 +785,12 @@ func (vm *VMKeeper) QueryEvalString(ctx sdk.Context, pkgPath string, expr string } m := gno.NewMachineWithOptions( gno.MachineOptions{ - PkgPath: pkgPath, - Output: os.Stdout, // XXX - Store: gnostore, - Context: msgCtx, - Alloc: alloc, - MaxCycles: vm.maxCycles, - GasMeter: ctx.GasMeter(), + PkgPath: pkgPath, + Output: os.Stdout, // XXX + Store: gnostore, + Context: msgCtx, + Alloc: alloc, + GasMeter: ctx.GasMeter(), }) defer m.Release() defer func() { From e573b6a59764f26d46ca512c4208cef88b797f39 Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Mon, 21 Oct 2024 12:31:56 -0500 Subject: [PATCH 09/24] chore: fixup Signed-off-by: moul <94029+moul@users.noreply.github.com> --- gno.land/pkg/gnoland/validators.go | 61 ++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 gno.land/pkg/gnoland/validators.go diff --git a/gno.land/pkg/gnoland/validators.go b/gno.land/pkg/gnoland/validators.go new file mode 100644 index 00000000000..1843dff3984 --- /dev/null +++ b/gno.land/pkg/gnoland/validators.go @@ -0,0 +1,61 @@ +package gnoland + +import ( + "regexp" + + gnovm "github.com/gnolang/gno/gnovm/stdlibs/std" + "github.com/gnolang/gno/tm2/pkg/bft/types" + "github.com/gnolang/gno/tm2/pkg/events" +) + +const ( + valRealm = "gno.land/r/sys/validators" + valChangesFn = "GetChanges" + + validatorAddedEvent = "ValidatorAdded" + validatorRemovedEvent = "ValidatorRemoved" +) + +// XXX: replace with amino-based clean approach +var valRegexp = regexp.MustCompile(`{\("([^"]*)"\s[^)]+\),\("((?:[^"]|\\")*)"\s[^)]+\),\((\d+)\s[^)]+\)}`) + +// validatorUpdate is a type being used for "notifying" +// that a validator change happened on-chain. The events from `r/sys/validators` +// do not pass data related to validator add / remove instances (who, what, how) +type validatorUpdate struct{} + +// validatorEventFilter filters the given event to determine if it +// is tied to a validator update +func validatorEventFilter(event events.Event) []validatorUpdate { + // Make sure the event is a new TX event + txResult, ok := event.(types.EventTx) + if !ok { + return nil + } + + // Make sure an add / remove event happened + for _, ev := range txResult.Result.Response.Events { + // Make sure the event is a GnoVM event + gnoEv, ok := ev.(gnovm.GnoEvent) + if !ok { + continue + } + + // Make sure the event is from `r/sys/validators` + if gnoEv.PkgPath != valRealm { + continue + } + + // Make sure the event is either an add / remove + switch gnoEv.Type { + case validatorAddedEvent, validatorRemovedEvent: + // We don't pass data around with the events, but a single + // notification is enough to "trigger" a VM scrape + return []validatorUpdate{{}} + default: + continue + } + } + + return nil +} From 993d2130dce9a58fd6ad8734ceb83e83fc36eb7f Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Mon, 21 Oct 2024 12:43:03 -0500 Subject: [PATCH 10/24] chore: fixup Signed-off-by: moul <94029+moul@users.noreply.github.com> --- gno.land/pkg/sdk/vm/common_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gno.land/pkg/sdk/vm/common_test.go b/gno.land/pkg/sdk/vm/common_test.go index 43a8fe1fbec..66975fba923 100644 --- a/gno.land/pkg/sdk/vm/common_test.go +++ b/gno.land/pkg/sdk/vm/common_test.go @@ -47,7 +47,7 @@ func _setupTestEnv(cacheStdlibs bool) testEnv { ctx := sdk.NewContext(sdk.RunTxModeDeliver, ms, &bft.Header{ChainID: "test-chain-id"}, log.NewNoopLogger()) acck := authm.NewAccountKeeper(iavlCapKey, std.ProtoBaseAccount) bank := bankm.NewBankKeeper(acck) - vmk := NewVMKeeper(baseCapKey, iavlCapKey, acck, bank, 100_000_000) + vmk := NewVMKeeper(baseCapKey, iavlCapKey, acck, bank) mcw := ms.MultiCacheWrap() vmk.Initialize(log.NewNoopLogger(), mcw) From 95384ae6a9267e6d0f40a9fcfa58a63808f75d48 Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Mon, 21 Oct 2024 20:27:18 -0500 Subject: [PATCH 11/24] chore: fixup Signed-off-by: moul <94029+moul@users.noreply.github.com> --- tm2/pkg/sdk/params/consts.go | 6 - tm2/pkg/sdk/params/keeper.go | 189 ++++++++++++------------- tm2/pkg/sdk/params/keeper_test.go | 226 +++++++++++------------------- tm2/pkg/sdk/params/paramset.go | 25 ---- tm2/pkg/sdk/params/table.go | 77 ---------- 5 files changed, 170 insertions(+), 353 deletions(-) delete mode 100644 tm2/pkg/sdk/params/consts.go delete mode 100644 tm2/pkg/sdk/params/paramset.go delete mode 100644 tm2/pkg/sdk/params/table.go diff --git a/tm2/pkg/sdk/params/consts.go b/tm2/pkg/sdk/params/consts.go deleted file mode 100644 index eb829a08b73..00000000000 --- a/tm2/pkg/sdk/params/consts.go +++ /dev/null @@ -1,6 +0,0 @@ -package params - -const ( - ModuleName = "params" - StoreKey = ModuleName -) diff --git a/tm2/pkg/sdk/params/keeper.go b/tm2/pkg/sdk/params/keeper.go index 0f7c14b5f47..ffeb1775acb 100644 --- a/tm2/pkg/sdk/params/keeper.go +++ b/tm2/pkg/sdk/params/keeper.go @@ -1,19 +1,36 @@ package params import ( - "fmt" "log/slog" - "maps" - "reflect" + "strings" "github.com/gnolang/gno/tm2/pkg/amino" "github.com/gnolang/gno/tm2/pkg/sdk" "github.com/gnolang/gno/tm2/pkg/store" ) +const ( + ModuleName = "params" + StoreKey = ModuleName +) + type ParamsKeeperI interface { - Get(ctx sdk.Context, key string, ptr interface{}) - Set(ctx sdk.Context, key string, value interface{}) + GetString(ctx sdk.Context, key string, ptr *string) + GetInt64(ctx sdk.Context, key string, ptr *int64) + GetUint64(ctx sdk.Context, key string, ptr *uint64) + GetBool(ctx sdk.Context, key string, ptr *bool) + GetBytes(ctx sdk.Context, key string, ptr *[]byte) + + SetString(ctx sdk.Context, key string, value string) + SetInt64(ctx sdk.Context, key string, value int64) + SetUint64(ctx sdk.Context, key string, value uint64) + SetBool(ctx sdk.Context, key string, value bool) + SetBytes(ctx sdk.Context, key string, value []byte) + + Has(ctx sdk.Context, key string) bool + GetRaw(ctx sdk.Context, key string) []byte + + // XXX: ListKeys? } var _ ParamsKeeperI = ParamsKeeper{} @@ -21,7 +38,6 @@ var _ ParamsKeeperI = ParamsKeeper{} // global paramstore Keeper. type ParamsKeeper struct { key store.StoreKey - table KeyTable prefix string } @@ -29,7 +45,6 @@ type ParamsKeeper struct { func NewParamsKeeper(key store.StoreKey, prefix string) ParamsKeeper { return ParamsKeeper{ key: key, - table: NewKeyTable(), prefix: prefix, } } @@ -45,124 +60,98 @@ func (pk ParamsKeeper) Has(ctx sdk.Context, key string) bool { return stor.Has([]byte(key)) } -func (pk ParamsKeeper) Get(ctx sdk.Context, key string, ptr interface{}) { - pk.checkType(key, ptr) - stor := ctx.Store(pk.key) - bz := stor.Get([]byte(key)) - err := amino.UnmarshalJSON(bz, ptr) - if err != nil { - panic(err) - } -} - -func (pk ParamsKeeper) GetIfExists(ctx sdk.Context, key string, ptr interface{}) { - stor := ctx.Store(pk.key) - bz := stor.Get([]byte(key)) - if bz == nil { - return - } - pk.checkType(key, ptr) - err := amino.UnmarshalJSON(bz, ptr) - if err != nil { - panic(err) - } -} - func (pk ParamsKeeper) GetRaw(ctx sdk.Context, key string) []byte { stor := ctx.Store(pk.key) return stor.Get([]byte(key)) } -func (pk ParamsKeeper) Set(ctx sdk.Context, key string, value interface{}) { - pk.checkType(key, value) - stor := ctx.Store(pk.key) - bz, err := amino.MarshalJSON(value) - if err != nil { - panic(err) - } - stor.Set([]byte(key), bz) +func (pk ParamsKeeper) GetString(ctx sdk.Context, key string, ptr *string) { + checkSuffix(key, ".string") + pk.getIfExists(ctx, key, ptr) } -func (pk ParamsKeeper) Update(ctx sdk.Context, key string, value []byte) error { - attr, ok := pk.table.m[key] - if !ok { - panic(fmt.Sprintf("parameter %s not registered", key)) - } - - ty := attr.ty - dest := reflect.New(ty).Interface() - pk.GetIfExists(ctx, key, dest) - - if err := amino.UnmarshalJSON(value, dest); err != nil { - return err - } - - destValue := reflect.Indirect(reflect.ValueOf(dest)).Interface() - if err := pk.Validate(ctx, key, destValue); err != nil { - return err - } +func (pk ParamsKeeper) GetBool(ctx sdk.Context, key string, ptr *bool) { + checkSuffix(key, ".bool") + pk.getIfExists(ctx, key, ptr) +} - pk.Set(ctx, key, dest) - return nil +func (pk ParamsKeeper) GetInt64(ctx sdk.Context, key string, ptr *int64) { + checkSuffix(key, ".int64") + pk.getIfExists(ctx, key, ptr) } -func (pk ParamsKeeper) Validate(ctx sdk.Context, key string, value interface{}) error { - attr, ok := pk.table.m[key] - if !ok { - return fmt.Errorf("parameter %s not registered", key) - } +func (pk ParamsKeeper) GetUint64(ctx sdk.Context, key string, ptr *uint64) { + checkSuffix(key, ".uint64") + pk.getIfExists(ctx, key, ptr) +} - if err := attr.vfn(value); err != nil { - return fmt.Errorf("invalid parameter value: %w", err) - } +func (pk ParamsKeeper) GetBytes(ctx sdk.Context, key string, ptr *[]byte) { + checkSuffix(key, ".bytes") + pk.getIfExists(ctx, key, ptr) +} - return nil +func (pk ParamsKeeper) SetString(ctx sdk.Context, key, value string) { + checkSuffix(key, ".string") + pk.set(ctx, key, value) } -func (pk ParamsKeeper) checkType(key string, value interface{}) { - attr, ok := pk.table.m[key] - if !ok { - panic(fmt.Sprintf("parameter %s is not registered", key)) - } +func (pk ParamsKeeper) SetBool(ctx sdk.Context, key string, value bool) { + checkSuffix(key, ".bool") + pk.set(ctx, key, value) +} - ty := attr.ty - pty := reflect.TypeOf(value) - if pty.Kind() == reflect.Ptr { - pty = pty.Elem() - } +func (pk ParamsKeeper) SetInt64(ctx sdk.Context, key string, value int64) { + checkSuffix(key, ".int64") + pk.set(ctx, key, value) +} - if pty != ty { - panic("type mismatch with registered table") - } +func (pk ParamsKeeper) SetUint64(ctx sdk.Context, key string, value uint64) { + checkSuffix(key, ".uint64") + pk.set(ctx, key, value) } -func (pk ParamsKeeper) HasKeyTable() bool { - return len(pk.table.m) > 0 +func (pk ParamsKeeper) SetBytes(ctx sdk.Context, key string, value []byte) { + checkSuffix(key, ".bytes") + pk.set(ctx, key, value) } -func (pk ParamsKeeper) WithKeyTable(table KeyTable) ParamsKeeper { - if table.m == nil { - panic("WithKeyTable() called with nil KeyTable") +func (pk ParamsKeeper) getIfExists(ctx sdk.Context, key string, ptr interface{}) { + stor := ctx.Store(pk.key) + bz := stor.Get([]byte(key)) + if bz == nil { + return } - if len(pk.table.m) != 0 { - panic("WithKeyTable() called on already initialized Keeper") + err := amino.UnmarshalJSON(bz, ptr) + if err != nil { + panic(err) } - - maps.Copy(pk.table.m, table.m) - return pk } -// XXX: added, should we remove? -func (pk ParamsKeeper) RegisterType(psp ParamSetPair) { - pk.table.RegisterType(psp) +func (pk ParamsKeeper) get(ctx sdk.Context, key string, ptr interface{}) { + stor := ctx.Store(pk.key) + bz := stor.Get([]byte(key)) + err := amino.UnmarshalJSON(bz, ptr) + if err != nil { + panic(err) + } } -// XXX: added, should we remove? -func (pk ParamsKeeper) HasTypeKey(key string) bool { - return pk.table.HasKey(key) +func (pk ParamsKeeper) set(ctx sdk.Context, key string, value interface{}) { + stor := ctx.Store(pk.key) + bz, err := amino.MarshalJSON(value) + if err != nil { + panic(err) + } + stor.Set([]byte(key), bz) } -// XXX: GetAllKeys -// XXX: GetAllParams -// XXX: ViewKeeper -// XXX: ModuleKeeper +func checkSuffix(key, expectedSuffix string) { + var ( + noSuffix = !strings.HasSuffix(key, expectedSuffix) + noName = len(key) == len(expectedSuffix) + // XXX: additional sanity checks? + ) + if noSuffix || noName { + panic(`key should be like "` + expectedSuffix + `"`) + } +} diff --git a/tm2/pkg/sdk/params/keeper_test.go b/tm2/pkg/sdk/params/keeper_test.go index 6fd1e6ae65b..a0435f03bb3 100644 --- a/tm2/pkg/sdk/params/keeper_test.go +++ b/tm2/pkg/sdk/params/keeper_test.go @@ -8,91 +8,87 @@ import ( "github.com/stretchr/testify/require" ) -// XXX: transient test - func TestKeeper(t *testing.T) { - kvs := []struct { - key string - param int64 - }{ - {"key1", 10}, - {"key2", 55}, - {"key3", 182}, - {"key4", 17582}, - {"key5", 2768554}, - {"key6", 1157279}, - {"key7", 9058701}, - } - - table := NewKeyTable( - NewParamSetPair("key1", int64(0), validateNoOp), - NewParamSetPair("key2", int64(0), validateNoOp), - NewParamSetPair("key3", int64(0), validateNoOp), - NewParamSetPair("key4", int64(0), validateNoOp), - NewParamSetPair("key5", int64(0), validateNoOp), - NewParamSetPair("key6", int64(0), validateNoOp), - NewParamSetPair("key7", int64(0), validateNoOp), - NewParamSetPair("extra1", bool(false), validateNoOp), - NewParamSetPair("extra2", string(""), validateNoOp), - ) - env := setupTestEnv() ctx, store, keeper := env.ctx, env.store, env.keeper + _ = store // XXX: add store tests? + + require.False(t, keeper.Has(ctx, "param1.string")) + require.False(t, keeper.Has(ctx, "param2.bool")) + require.False(t, keeper.Has(ctx, "param3.uint64")) + require.False(t, keeper.Has(ctx, "param4.int64")) + require.False(t, keeper.Has(ctx, "param5.bytes")) + + // initial set + require.NotPanics(t, func() { keeper.SetString(ctx, "param1.string", "foo") }) + require.NotPanics(t, func() { keeper.SetBool(ctx, "param2.bool", true) }) + require.NotPanics(t, func() { keeper.SetUint64(ctx, "param3.uint64", 42) }) + require.NotPanics(t, func() { keeper.SetInt64(ctx, "param4.int64", -1337) }) + require.NotPanics(t, func() { keeper.SetBytes(ctx, "param5.bytes", []byte("hello world!")) }) + + require.True(t, keeper.Has(ctx, "param1.string")) + require.True(t, keeper.Has(ctx, "param2.bool")) + require.True(t, keeper.Has(ctx, "param3.uint64")) + require.True(t, keeper.Has(ctx, "param4.int64")) + require.True(t, keeper.Has(ctx, "param5.bytes")) + + var ( + param1 string + param2 bool + param3 uint64 + param4 int64 + param5 []byte + ) - require.False(t, keeper.HasKeyTable()) - keeper = keeper.WithKeyTable(table) - require.True(t, keeper.HasKeyTable()) - - // Set params - for i, kv := range kvs { - require.NotPanics(t, func() { keeper.Set(ctx, kv.key, kv.param) }, "keeper.Set panics, tc #%d", i) - } - - // Test keeper.Get - for i, kv := range kvs { - var param int64 - require.NotPanics(t, func() { keeper.Get(ctx, kv.key, ¶m) }, "keeper.Get panics, tc #%d", i) - require.Equal(t, kv.param, param, "stored param not equal, tc #%d", i) - } - - // Test keeper.GetRaw - for i, kv := range kvs { - var param int64 - bz := keeper.GetRaw(ctx, kv.key) - err := amino.UnmarshalJSON(bz, ¶m) - require.Nil(t, err, "err is not nil, tc #%d", i) - require.Equal(t, kv.param, param, "stored param not equal, tc #%d", i) - } - - // Test store.Get equals keeper.Get - for i, kv := range kvs { - var param int64 - bz := store.Get([]byte(kv.key)) - require.NotNil(t, bz, "KVStore.Get returns nil, tc #%d", i) - err := amino.UnmarshalJSON(bz, ¶m) - require.NoError(t, err, "UnmarshalJSON returns error, tc #%d", i) - require.Equal(t, kv.param, param, "stored param not equal, tc #%d", i) - } - - // Test invalid keeper.Get - for i, kv := range kvs { - var param bool - require.Panics(t, func() { keeper.Get(ctx, kv.key, ¶m) }, "invalid keeper.Get not panics, tc #%d", i) - } - - // Test invalid keeper.Set - for i, kv := range kvs { - require.Panics(t, func() { keeper.Set(ctx, kv.key, true) }, "invalid keeper.Set not panics, tc #%d", i) - } + require.NotPanics(t, func() { keeper.GetString(ctx, "param1.string", ¶m1) }) + require.NotPanics(t, func() { keeper.GetBool(ctx, "param2.bool", ¶m2) }) + require.NotPanics(t, func() { keeper.GetUint64(ctx, "param3.uint64", ¶m3) }) + require.NotPanics(t, func() { keeper.GetInt64(ctx, "param4.int64", ¶m4) }) + require.NotPanics(t, func() { keeper.GetBytes(ctx, "param5.bytes", ¶m5) }) + + require.Equal(t, param1, "foo") + require.Equal(t, param2, true) + require.Equal(t, param3, uint64(42)) + require.Equal(t, param4, int64(-1337)) + require.Equal(t, param5, []byte("hello world!")) + + // reset + require.NotPanics(t, func() { keeper.SetString(ctx, "param1.string", "bar") }) + require.NotPanics(t, func() { keeper.SetBool(ctx, "param2.bool", false) }) + require.NotPanics(t, func() { keeper.SetUint64(ctx, "param3.uint64", 12345) }) + require.NotPanics(t, func() { keeper.SetInt64(ctx, "param4.int64", 1000) }) + require.NotPanics(t, func() { keeper.SetBytes(ctx, "param5.bytes", []byte("bye")) }) + + require.True(t, keeper.Has(ctx, "param1.string")) + require.True(t, keeper.Has(ctx, "param2.bool")) + require.True(t, keeper.Has(ctx, "param3.uint64")) + require.True(t, keeper.Has(ctx, "param4.int64")) + require.True(t, keeper.Has(ctx, "param5.bytes")) + + require.NotPanics(t, func() { keeper.GetString(ctx, "param1.string", ¶m1) }) + require.NotPanics(t, func() { keeper.GetBool(ctx, "param2.bool", ¶m2) }) + require.NotPanics(t, func() { keeper.GetUint64(ctx, "param3.uint64", ¶m3) }) + require.NotPanics(t, func() { keeper.GetInt64(ctx, "param4.int64", ¶m4) }) + require.NotPanics(t, func() { keeper.GetBytes(ctx, "param5.bytes", ¶m5) }) + + require.Equal(t, param1, "bar") + require.Equal(t, param2, false) + require.Equal(t, param3, uint64(12345)) + require.Equal(t, param4, int64(1000)) + require.Equal(t, param5, []byte("bye")) + + // invalid sets + require.PanicsWithValue(t, `key should be like ".string"`, func() { keeper.SetString(ctx, "invalid.int64", "hello") }) + require.PanicsWithValue(t, `key should be like ".int64"`, func() { keeper.SetInt64(ctx, "invalid.string", int64(42)) }) + require.PanicsWithValue(t, `key should be like ".uint64"`, func() { keeper.SetUint64(ctx, "invalid.int64", uint64(42)) }) + require.PanicsWithValue(t, `key should be like ".bool"`, func() { keeper.SetBool(ctx, "invalid.int64", true) }) + require.PanicsWithValue(t, `key should be like ".bytes"`, func() { keeper.SetBytes(ctx, "invalid.int64", []byte("hello")) }) } // adapted from TestKeeperSubspace from Cosmos SDK, but adapted to a subspace-less Keeper. -func TestKeeper_Subspace(t *testing.T) { +func TestKeeper_internal(t *testing.T) { env := setupTestEnv() ctx, store, keeper := env.ctx, env.store, env.keeper - // XXX: keeper = keeper.WithKeyTable(table) - - // cdc, ctx, key, _, keeper := testComponents() kvs := []struct { key string @@ -108,55 +104,30 @@ func TestKeeper_Subspace(t *testing.T) { {"uint16", uint16(1), uint16(0), new(uint16)}, {"uint32", uint32(1), uint32(0), new(uint32)}, {"uint64", uint64(1), uint64(0), new(uint64)}, - // XXX: {"int", math.NewInt(1), math.Int{}, new(math.Int)}, - // XXX: {"uint", math.NewUint(1), math.Uint{}, new(math.Uint)}, - // XXX: {"dec", math.LegacyNewDec(1), math.LegacyDec{}, new(math.LegacyDec)}, {"struct", s{1}, s{0}, new(s)}, } - table := NewKeyTable( - NewParamSetPair("string", "", validateNoOp), - NewParamSetPair("bool", false, validateNoOp), - NewParamSetPair("int16", int16(0), validateNoOp), - NewParamSetPair("int32", int32(0), validateNoOp), - NewParamSetPair("int64", int64(0), validateNoOp), - NewParamSetPair("uint16", uint16(0), validateNoOp), - NewParamSetPair("uint32", uint32(0), validateNoOp), - NewParamSetPair("uint64", uint64(0), validateNoOp), - // XXX: NewParamSetPair("int", math.Int{}, validateNoOp), - // XXX: NewParamSetPair("uint", math.Uint{}, validateNoOp), - // XXX: NewParamSetPair("dec", math.LegacyDec{}, validateNoOp), - NewParamSetPair("struct", s{}, validateNoOp), - ) - keeper = keeper.WithKeyTable(table) - - // Test keeper.Set, keeper.Modified for i, kv := range kvs { - // require.False(t, keeper.Modified(ctx, kv.key), "keeper.Modified returns true before setting, tc #%d", i) - require.NotPanics(t, func() { keeper.Set(ctx, kv.key, kv.param) }, "keeper.Set panics, tc #%d", i) - // require.True(t, keeper.Modified(ctx, kv.key), "keeper.Modified returns false after setting, tc #%d", i) + require.NotPanics(t, func() { keeper.set(ctx, kv.key, kv.param) }, "keeper.Set panics, tc #%d", i) } - // Test keeper.Get, keeper.GetIfExists for i, kv := range kvs { - require.NotPanics(t, func() { keeper.GetIfExists(ctx, "invalid", kv.ptr) }, "keeper.GetIfExists panics when no value exists, tc #%d", i) + require.NotPanics(t, func() { keeper.getIfExists(ctx, "invalid", kv.ptr) }, "keeper.GetIfExists panics when no value exists, tc #%d", i) require.Equal(t, kv.zero, indirect(kv.ptr), "keeper.GetIfExists unmarshalls when no value exists, tc #%d", i) - require.Panics(t, func() { keeper.Get(ctx, "invalid", kv.ptr) }, "invalid keeper.Get not panics when no value exists, tc #%d", i) + require.Panics(t, func() { keeper.get(ctx, "invalid", kv.ptr) }, "invalid keeper.Get not panics when no value exists, tc #%d", i) require.Equal(t, kv.zero, indirect(kv.ptr), "invalid keeper.Get unmarshalls when no value exists, tc #%d", i) - require.NotPanics(t, func() { keeper.GetIfExists(ctx, kv.key, kv.ptr) }, "keeper.GetIfExists panics, tc #%d", i) + require.NotPanics(t, func() { keeper.getIfExists(ctx, kv.key, kv.ptr) }, "keeper.GetIfExists panics, tc #%d", i) require.Equal(t, kv.param, indirect(kv.ptr), "stored param not equal, tc #%d", i) - require.NotPanics(t, func() { keeper.Get(ctx, kv.key, kv.ptr) }, "keeper.Get panics, tc #%d", i) + require.NotPanics(t, func() { keeper.get(ctx, kv.key, kv.ptr) }, "keeper.Get panics, tc #%d", i) require.Equal(t, kv.param, indirect(kv.ptr), "stored param not equal, tc #%d", i) - require.Panics(t, func() { keeper.Get(ctx, "invalid", kv.ptr) }, "invalid keeper.Get not panics when no value exists, tc #%d", i) + require.Panics(t, func() { keeper.get(ctx, "invalid", kv.ptr) }, "invalid keeper.Get not panics when no value exists, tc #%d", i) require.Equal(t, kv.param, indirect(kv.ptr), "invalid keeper.Get unmarshalls when no value existt, tc #%d", i) - require.Panics(t, func() { keeper.Get(ctx, kv.key, nil) }, "invalid keeper.Get not panics when the pointer is nil, tc #%d", i) - require.Panics(t, func() { keeper.Get(ctx, kv.key, new(invalid)) }, "invalid keeper.Get not panics when the pointer is different type, tc #%d", i) + require.Panics(t, func() { keeper.get(ctx, kv.key, nil) }, "invalid keeper.Get not panics when the pointer is nil, tc #%d", i) } - // Test store.Get equals keeper.Get for i, kv := range kvs { bz := store.Get([]byte(kv.key)) require.NotNil(t, bz, "store.Get() returns nil, tc #%d", i) @@ -166,44 +137,9 @@ func TestKeeper_Subspace(t *testing.T) { } } -func TestJSONUpdate(t *testing.T) { - env := setupTestEnv() - ctx, keeper := env.ctx, env.keeper - key := "key" - - space := keeper.WithKeyTable(NewKeyTable(NewParamSetPair(key, paramJSON{}, validateNoOp))) - - var param paramJSON - - err := space.Update(ctx, key, []byte(`{"param1": "10241024"}`)) - require.NoError(t, err) - space.Get(ctx, key, ¶m) - require.Equal(t, paramJSON{10241024, ""}, param) - - err = space.Update(ctx, key, []byte(`{"param2": "helloworld"}`)) - require.NoError(t, err) - space.Get(ctx, key, ¶m) - require.Equal(t, paramJSON{10241024, "helloworld"}, param) - - err = space.Update(ctx, key, []byte(`{"param1": "20482048"}`)) - require.NoError(t, err) - space.Get(ctx, key, ¶m) - require.Equal(t, paramJSON{20482048, "helloworld"}, param) - - err = space.Update(ctx, key, []byte(`{"param1": "40964096", "param2": "goodbyeworld"}`)) - require.NoError(t, err) - space.Get(ctx, key, ¶m) - require.Equal(t, paramJSON{40964096, "goodbyeworld"}, param) -} - type ( - invalid struct{} - s struct{ I int } - paramJSON struct { - Param1 int64 `json:"param1,omitempty" yaml:"param1,omitempty"` - Param2 string `json:"param2,omitempty" yaml:"param2,omitempty"` - } + invalid struct{} + s struct{ I int } ) -func validateNoOp(_ interface{}) error { return nil } func indirect(ptr interface{}) interface{} { return reflect.ValueOf(ptr).Elem().Interface() } diff --git a/tm2/pkg/sdk/params/paramset.go b/tm2/pkg/sdk/params/paramset.go deleted file mode 100644 index b401c56e69e..00000000000 --- a/tm2/pkg/sdk/params/paramset.go +++ /dev/null @@ -1,25 +0,0 @@ -package params - -// This file mirrors the original implementation from the Cosmos SDK. - -type ValueValidatorFn func(value interface{}) error - -// ParamSetPair is used for associating param key and field of param structs. -type ParamSetPair struct { - Key string - Value interface{} - ValidatorFn ValueValidatorFn -} - -// NewParamSetPair creates a new ParamSetPair instance. -func NewParamSetPair(key string, value interface{}, vfn ValueValidatorFn) ParamSetPair { - return ParamSetPair{key, value, vfn} -} - -// ParamSetPairs Slice of KeyFieldPair. -type ParamSetPairs []ParamSetPair - -// ParamSet defines an interface for structs containing parameters for a module. -type ParamSet interface { - ParamSetPairs() ParamSetPairs -} diff --git a/tm2/pkg/sdk/params/table.go b/tm2/pkg/sdk/params/table.go deleted file mode 100644 index dae3b0f62c6..00000000000 --- a/tm2/pkg/sdk/params/table.go +++ /dev/null @@ -1,77 +0,0 @@ -package params - -// This file closely mirrors the original implementation from the Cosmos SDK, with only minor modifications. - -import ( - "reflect" - "regexp" -) - -type attribute struct { - ty reflect.Type - vfn ValueValidatorFn -} - -// KeyTable subspaces appropriate type for each parameter key -type KeyTable struct { - m map[string]attribute -} - -func NewKeyTable(pairs ...ParamSetPair) KeyTable { - keyTable := KeyTable{ - m: make(map[string]attribute), - } - - for _, psp := range pairs { - keyTable = keyTable.RegisterType(psp) - } - - return keyTable -} - -// RegisterType registers a single ParamSetPair (key-type pair) in a KeyTable. -func (t KeyTable) RegisterType(psp ParamSetPair) KeyTable { - if len(psp.Key) == 0 { - panic("cannot register ParamSetPair with an parameter empty key") - } - // XXX: sanitize more? - /*if !isAlphaNumeric(psp.Key) { - panic("cannot register ParamSetPair with a non-alphanumeric parameter key") - }*/ - if psp.ValidatorFn == nil { - panic("cannot register ParamSetPair without a value validation function") - } - - if _, ok := t.m[psp.Key]; ok { - panic("duplicate parameter key: " + psp.Key) - } - - rty := reflect.TypeOf(psp.Value) - - // indirect rty if it is a pointer - for rty.Kind() == reflect.Ptr { - rty = rty.Elem() - } - - t.m[psp.Key] = attribute{ - vfn: psp.ValidatorFn, - ty: rty, - } - - return t -} - -// RegisterParamSet registers multiple ParamSetPairs from a ParamSet in a KeyTable. -func (t KeyTable) RegisterParamSet(ps ParamSet) KeyTable { - for _, psp := range ps.ParamSetPairs() { - t = t.RegisterType(psp) - } - return t -} - -func (t KeyTable) HasKey(key string) bool { - _, ok := t.m[key] - return ok -} - -var isAlphaNumeric = regexp.MustCompile(`^[a-zA-Z0-9]+$`).MatchString From a4608ebdc107977f47e7983bbf847c0a8794e3ef Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Mon, 21 Oct 2024 21:09:44 -0500 Subject: [PATCH 12/24] chore: fixup Signed-off-by: moul <94029+moul@users.noreply.github.com> --- gno.land/pkg/sdk/vm/builtins.go | 42 ++++-------------------------- gno.land/pkg/sdk/vm/keeper.go | 10 ++++--- gno.land/pkg/sdk/vm/keeper_test.go | 4 +-- 3 files changed, 13 insertions(+), 43 deletions(-) diff --git a/gno.land/pkg/sdk/vm/builtins.go b/gno.land/pkg/sdk/vm/builtins.go index cbd4eb14539..161e459873d 100644 --- a/gno.land/pkg/sdk/vm/builtins.go +++ b/gno.land/pkg/sdk/vm/builtins.go @@ -3,7 +3,6 @@ package vm import ( "github.com/gnolang/gno/tm2/pkg/crypto" "github.com/gnolang/gno/tm2/pkg/sdk" - "github.com/gnolang/gno/tm2/pkg/sdk/params" "github.com/gnolang/gno/tm2/pkg/std" ) @@ -72,41 +71,10 @@ func NewSDKParams(vmk *VMKeeper, ctx sdk.Context) *SDKParams { } } -// SetXXX helpers: -// - dynamically register a new key with the corresponding type in the paramset table (only once). -// - set the value. - -func (prm *SDKParams) SetString(key, value string) { - // if !prm.vmk.prmk.Has(prm.ctx, key) { - // XXX: bad workaround, maybe we should have a dedicated "dynamic keeper" allowing to create keys on the go? - if !prm.vmk.prmk.HasTypeKey(key) { - prm.vmk.prmk.RegisterType(params.NewParamSetPair(key, "", validateNoOp)) - } - prm.vmk.prmk.Set(prm.ctx, key, value) -} - -func (prm *SDKParams) SetBool(key string, value bool) { - // if !prm.vmk.prmk.Has(prm.ctx, key) { - if !prm.vmk.prmk.HasTypeKey(key) { - prm.vmk.prmk.RegisterType(params.NewParamSetPair(key, true, validateNoOp)) - } - prm.vmk.prmk.Set(prm.ctx, key, value) -} - -func (prm *SDKParams) SetInt64(key string, value int64) { - // if !prm.vmk.prmk.Has(prm.ctx, key) { - if !prm.vmk.prmk.HasTypeKey(key) { - prm.vmk.prmk.RegisterType(params.NewParamSetPair(key, int64(0), validateNoOp)) - } - prm.vmk.prmk.Set(prm.ctx, key, value) -} - +func (prm *SDKParams) SetString(key, value string) { prm.vmk.prmk.SetString(prm.ctx, key, value) } +func (prm *SDKParams) SetBool(key string, value bool) { prm.vmk.prmk.SetBool(prm.ctx, key, value) } +func (prm *SDKParams) SetInt64(key string, value int64) { prm.vmk.prmk.SetInt64(prm.ctx, key, value) } func (prm *SDKParams) SetUint64(key string, value uint64) { - // if !prm.vmk.prmk.Has(prm.ctx, key) { - if !prm.vmk.prmk.HasTypeKey(key) { - prm.vmk.prmk.RegisterType(params.NewParamSetPair(key, uint64(0), validateNoOp)) - } - prm.vmk.prmk.Set(prm.ctx, key, value) + prm.vmk.prmk.SetUint64(prm.ctx, key, value) } - -func validateNoOp(_ interface{}) error { return nil } +func (prm *SDKParams) SetBytes(key string, value []byte) { prm.vmk.prmk.SetBytes(prm.ctx, key, value) } diff --git a/gno.land/pkg/sdk/vm/keeper.go b/gno.land/pkg/sdk/vm/keeper.go index 573d9163ac3..ef1705c7ae9 100644 --- a/gno.land/pkg/sdk/vm/keeper.go +++ b/gno.land/pkg/sdk/vm/keeper.go @@ -225,13 +225,15 @@ func (vm *VMKeeper) getGnoTransactionStore(ctx sdk.Context) gno.TransactionStore // Namespace can be either a user or crypto address. var reNamespace = regexp.MustCompile(`^gno.land/(?:r|p)/([\.~_a-zA-Z0-9]+)`) -const sysUsersPkgParamKey = "vm/gno.land/r/sys/params.string" -const defaultSysUsersPkg = "gno.land/r/sys/users" +const ( + sysUsersPkgParamKey = "vm/gno.land/r/sys/params.string" + sysUsersPkgDefault = "gno.land/r/sys/users" +) // checkNamespacePermission check if the user as given has correct permssion to on the given pkg path func (vm *VMKeeper) checkNamespacePermission(ctx sdk.Context, creator crypto.Address, pkgPath string) error { - sysUsersPkg := defaultSysUsersPkg - vm.prmk.GetIfExists(ctx, sysUsersPkgParamKey, &sysUsersPkg) + sysUsersPkg := sysUsersPkgDefault + vm.prmk.GetString(ctx, sysUsersPkgParamKey, &sysUsersPkg) store := vm.getGnoTransactionStore(ctx) diff --git a/gno.land/pkg/sdk/vm/keeper_test.go b/gno.land/pkg/sdk/vm/keeper_test.go index 37fbb0af4f7..a6faa410009 100644 --- a/gno.land/pkg/sdk/vm/keeper_test.go +++ b/gno.land/pkg/sdk/vm/keeper_test.go @@ -346,8 +346,8 @@ func Do() string { var foo string var bar int64 - env.vmk.prmk.Get(ctx, "gno.land/r/test.foo.string", &foo) - env.vmk.prmk.Get(ctx, "gno.land/r/test.bar.int64", &bar) + env.vmk.prmk.GetString(ctx, "gno.land/r/test.foo.string", &foo) + env.vmk.prmk.GetInt64(ctx, "gno.land/r/test.bar.int64", &bar) assert.Equal(t, "foo2", foo) assert.Equal(t, int64(1337), bar) } From 9f18bad9a58dfda6bf58f86389dfcbb982a8f975 Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Mon, 21 Oct 2024 21:29:11 -0500 Subject: [PATCH 13/24] chore: rename config -> param Signed-off-by: moul <94029+moul@users.noreply.github.com> --- gno.land/cmd/gnoland/testdata/params.txtar | 11 ++---- gno.land/pkg/sdk/vm/keeper_test.go | 8 ++--- gnovm/stdlibs/generated.go | 42 +++++++++++++++++----- gnovm/stdlibs/std/params.gno | 22 ++++++------ gnovm/stdlibs/std/params.go | 15 +++++--- 5 files changed, 63 insertions(+), 35 deletions(-) diff --git a/gno.land/cmd/gnoland/testdata/params.txtar b/gno.land/cmd/gnoland/testdata/params.txtar index 1c19bc854e1..817c616b76d 100644 --- a/gno.land/cmd/gnoland/testdata/params.txtar +++ b/gno.land/cmd/gnoland/testdata/params.txtar @@ -53,11 +53,6 @@ gnokey maketx call -pkgpath gno.land/r/sys/setter -func SetBaz -args 31337 -gas- gnokey query params/vm/gno.land/r/sys/setter.baz.int64 stdout 'data: "31337"' - - - -# XXX: create a non-sys package that should fail? - -- setter/setter.gno -- package setter @@ -65,6 +60,6 @@ import ( "std" ) -func SetFoo(newFoo string) { std.SetConfig("foo", newFoo) } -func SetBar(newBar bool) { std.SetConfig("bar", newBar) } -func SetBaz(newBaz int64) { std.SetConfig("baz", newBaz) } +func SetFoo(newFoo string) { std.SetParam("foo", newFoo) } +func SetBar(newBar bool) { std.SetParam("bar", newBar) } +func SetBaz(newBaz int64) { std.SetParam("baz", newBaz) } diff --git a/gno.land/pkg/sdk/vm/keeper_test.go b/gno.land/pkg/sdk/vm/keeper_test.go index a6faa410009..9ec74abbf7f 100644 --- a/gno.land/pkg/sdk/vm/keeper_test.go +++ b/gno.land/pkg/sdk/vm/keeper_test.go @@ -319,12 +319,12 @@ package test import "std" func init() { - std.SetConfig("foo", "foo1") + std.SetParam("foo", "foo1") } func Do() string { - std.SetConfig("bar", int64(1337)) - std.SetConfig("foo", "foo2") // override init + std.SetParam("bar", int64(1337)) + std.SetParam("foo", "foo2") // override init return "XXX" // return std.GetConfig("gno.land/r/test.foo"), if we want to expose std.GetConfig, maybe as a std.TestGetConfig }`}, @@ -374,7 +374,7 @@ import "std" var admin std.Address func init() { - admin = std.GetOrigCaller() + admin = std.GetOrigCaller() } func Echo(msg string) string { diff --git a/gnovm/stdlibs/generated.go b/gnovm/stdlibs/generated.go index eaafa58309e..b0788fc6d1b 100644 --- a/gnovm/stdlibs/generated.go +++ b/gnovm/stdlibs/generated.go @@ -722,7 +722,7 @@ var nativeFuncs = [...]NativeFunc{ }, { "std", - "setConfigString", + "setParamString", []gno.FieldTypeExpr{ {Name: gno.N("p0"), Type: gno.X("string")}, {Name: gno.N("p1"), Type: gno.X("string")}, @@ -741,14 +741,14 @@ var nativeFuncs = [...]NativeFunc{ gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0) gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 1, "")).TV, rp1) - libs_std.X_setConfigString( + libs_std.X_setParamString( m, p0, p1) }, }, { "std", - "setConfigBool", + "setParamBool", []gno.FieldTypeExpr{ {Name: gno.N("p0"), Type: gno.X("string")}, {Name: gno.N("p1"), Type: gno.X("bool")}, @@ -767,14 +767,14 @@ var nativeFuncs = [...]NativeFunc{ gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0) gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 1, "")).TV, rp1) - libs_std.X_setConfigBool( + libs_std.X_setParamBool( m, p0, p1) }, }, { "std", - "setConfigInt64", + "setParamInt64", []gno.FieldTypeExpr{ {Name: gno.N("p0"), Type: gno.X("string")}, {Name: gno.N("p1"), Type: gno.X("int64")}, @@ -793,14 +793,14 @@ var nativeFuncs = [...]NativeFunc{ gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0) gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 1, "")).TV, rp1) - libs_std.X_setConfigInt64( + libs_std.X_setParamInt64( m, p0, p1) }, }, { "std", - "setConfigUint64", + "setParamUint64", []gno.FieldTypeExpr{ {Name: gno.N("p0"), Type: gno.X("string")}, {Name: gno.N("p1"), Type: gno.X("uint64")}, @@ -819,7 +819,33 @@ var nativeFuncs = [...]NativeFunc{ gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0) gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 1, "")).TV, rp1) - libs_std.X_setConfigUint64( + libs_std.X_setParamUint64( + m, + p0, p1) + }, + }, + { + "std", + "setParamBytes", + []gno.FieldTypeExpr{ + {Name: gno.N("p0"), Type: gno.X("string")}, + {Name: gno.N("p1"), Type: gno.X("[]byte")}, + }, + []gno.FieldTypeExpr{}, + true, + func(m *gno.Machine) { + b := m.LastBlock() + var ( + p0 string + rp0 = reflect.ValueOf(&p0).Elem() + p1 []byte + rp1 = reflect.ValueOf(&p1).Elem() + ) + + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0) + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 1, "")).TV, rp1) + + libs_std.X_setParamBytes( m, p0, p1) }, diff --git a/gnovm/stdlibs/std/params.gno b/gnovm/stdlibs/std/params.gno index a3dc47d4bd4..febc774c70e 100644 --- a/gnovm/stdlibs/std/params.gno +++ b/gnovm/stdlibs/std/params.gno @@ -1,28 +1,30 @@ package std // These are native bindings to the banker's functions. -func setConfigString(key string, val string) -func setConfigBool(key string, val bool) -func setConfigInt64(key string, val int64) -func setConfigUint64(key string, val uint64) +func setParamString(key string, val string) +func setParamBool(key string, val bool) +func setParamInt64(key string, val int64) +func setParamUint64(key string, val uint64) +func setParamBytes(key string, val []byte) // XXX: add doc -func SetConfig(key string, val interface{}) { +func SetParam(key string, val interface{}) { switch v := val.(type) { case string: - setConfigString(key, v) + setParamString(key, v) case bool: - setConfigBool(key, v) + setParamBool(key, v) case int64: - setConfigInt64(key, v) + setParamInt64(key, v) case uint64: - setConfigUint64(key, v) + setParamUint64(key, v) + case []byte: + setParamBytes(key, v) default: panic("unsupported type") } } -// XXX: add doc //func GetConfig(key string) string { // return getConfig(key) //} diff --git a/gnovm/stdlibs/std/params.go b/gnovm/stdlibs/std/params.go index 70ecbe6ba1d..36fcaf824af 100644 --- a/gnovm/stdlibs/std/params.go +++ b/gnovm/stdlibs/std/params.go @@ -17,29 +17,34 @@ type ParamsInterface interface { SetBool(key string, val bool) SetInt64(key string, val int64) SetUint64(key string, val uint64) - // XXX: GetString(key string) (string, error)? + SetBytes(key string, val []byte) } -func X_setConfigString(m *gno.Machine, key, val string) { +func X_setParamString(m *gno.Machine, key, val string) { pk := pkey(m, key, "string") GetContext(m).Params.SetString(pk, val) } -func X_setConfigBool(m *gno.Machine, key string, val bool) { +func X_setParamBool(m *gno.Machine, key string, val bool) { pk := pkey(m, key, "bool") GetContext(m).Params.SetBool(pk, val) } -func X_setConfigInt64(m *gno.Machine, key string, val int64) { +func X_setParamInt64(m *gno.Machine, key string, val int64) { pk := pkey(m, key, "int64") GetContext(m).Params.SetInt64(pk, val) } -func X_setConfigUint64(m *gno.Machine, key string, val uint64) { +func X_setParamUint64(m *gno.Machine, key string, val uint64) { pk := pkey(m, key, "uint64") GetContext(m).Params.SetUint64(pk, val) } +func X_setParamBytes(m *gno.Machine, key string, val []byte) { + pk := pkey(m, key, "bytes") + GetContext(m).Params.SetBytes(pk, val) +} + func pkey(m *gno.Machine, key string, kind string) string { // validate key. if len(key) == 0 { From 5ba066e8fc043c83ce6f6053dd504c198a285ec7 Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Mon, 21 Oct 2024 21:39:51 -0500 Subject: [PATCH 14/24] chore: fixup Signed-off-by: moul <94029+moul@users.noreply.github.com> --- tm2/pkg/sdk/params/keeper_test.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tm2/pkg/sdk/params/keeper_test.go b/tm2/pkg/sdk/params/keeper_test.go index a0435f03bb3..45a97ae44ad 100644 --- a/tm2/pkg/sdk/params/keeper_test.go +++ b/tm2/pkg/sdk/params/keeper_test.go @@ -137,9 +137,6 @@ func TestKeeper_internal(t *testing.T) { } } -type ( - invalid struct{} - s struct{ I int } -) +type s struct{ I int } func indirect(ptr interface{}) interface{} { return reflect.ValueOf(ptr).Elem().Interface() } From 52600590a3f3a4775f39849658f42a384e9e71aa Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Tue, 22 Oct 2024 09:14:57 -0500 Subject: [PATCH 15/24] chore: ignore generated files from codecov Signed-off-by: moul <94029+moul@users.noreply.github.com> --- .github/codecov.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/codecov.yml b/.github/codecov.yml index ea1c701d946..68bb808ea4f 100644 --- a/.github/codecov.yml +++ b/.github/codecov.yml @@ -39,3 +39,8 @@ flag_management: - type: patch target: auto # Let's decrease this later. threshold: 10 + +ignore: + - gnovm/stdlibs/generated.go + - gnovm/tests/stdlibs/generated.go + - **/*.pb.go From 309806f6b47a4efb2c0a55e529b7d7ef5e3235ab Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Tue, 22 Oct 2024 09:17:09 -0500 Subject: [PATCH 16/24] chore: fixup Signed-off-by: moul <94029+moul@users.noreply.github.com> --- .github/codecov.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/codecov.yml b/.github/codecov.yml index 68bb808ea4f..d1ecba7ade3 100644 --- a/.github/codecov.yml +++ b/.github/codecov.yml @@ -41,6 +41,6 @@ flag_management: threshold: 10 ignore: - - gnovm/stdlibs/generated.go - - gnovm/tests/stdlibs/generated.go - - **/*.pb.go + - "gnovm/stdlibs/generated.go" + - "gnovm/tests/stdlibs/generated.go" + - "**/*.pb.go" From 604466c0d4b228272e54892dbc00f5ee6ebab8f7 Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Tue, 22 Oct 2024 10:49:49 -0500 Subject: [PATCH 17/24] chore: fixup Signed-off-by: moul <94029+moul@users.noreply.github.com> --- gnovm/stdlibs/std/params.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gnovm/stdlibs/std/params.go b/gnovm/stdlibs/std/params.go index 36fcaf824af..f4cd8f7e245 100644 --- a/gnovm/stdlibs/std/params.go +++ b/gnovm/stdlibs/std/params.go @@ -48,15 +48,15 @@ func X_setParamBytes(m *gno.Machine, key string, val []byte) { func pkey(m *gno.Machine, key string, kind string) string { // validate key. if len(key) == 0 { - panic("empty param key") + m.Panic(typedString("empty param key")) } first := rune(key[0]) if !unicode.IsLetter(first) && first != '_' { - panic("invalid param key: " + key) + m.Panic(typedString("invalid param key: " + key)) } for _, char := range key[1:] { if !unicode.IsLetter(char) && !unicode.IsDigit(char) && char != '_' { - panic("invalid param key: " + key) + m.Panic(typedString("invalid param key: " + key)) } } From 8c5b00ab5f64e2827de143f3e98ad19d2159494e Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Tue, 22 Oct 2024 13:00:02 -0500 Subject: [PATCH 18/24] chore: fixup Signed-off-by: moul <94029+moul@users.noreply.github.com> --- tm2/pkg/sdk/params/handler_test.go | 58 ++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 tm2/pkg/sdk/params/handler_test.go diff --git a/tm2/pkg/sdk/params/handler_test.go b/tm2/pkg/sdk/params/handler_test.go new file mode 100644 index 00000000000..1fff5d007d3 --- /dev/null +++ b/tm2/pkg/sdk/params/handler_test.go @@ -0,0 +1,58 @@ +package params + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/require" + + abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types" + bft "github.com/gnolang/gno/tm2/pkg/bft/types" + "github.com/gnolang/gno/tm2/pkg/sdk" + tu "github.com/gnolang/gno/tm2/pkg/sdk/testutils" +) + +func TestInvalidMsg(t *testing.T) { + t.Parallel() + + h := NewHandler(ParamsKeeper{}) + res := h.Process(sdk.NewContext(sdk.RunTxModeDeliver, nil, &bft.Header{ChainID: "test-chain"}, nil), tu.NewTestMsg()) + require.False(t, res.IsOK()) + require.True(t, strings.Contains(res.Log, "unrecognized params message type")) +} + +func TestQuery(t *testing.T) { + t.Parallel() + + env := setupTestEnv() + h := NewHandler(env.keeper) + + req := abci.RequestQuery{ + Path: "params/params_test/foo/bar.string", + } + + res := h.Query(env.ctx, req) + require.Nil(t, res.Error) + require.NotNil(t, res) + require.Nil(t, res.Data) + + env.keeper.SetString(env.ctx, "foo/bar.string", "baz") + + res = h.Query(env.ctx, req) + require.Nil(t, res.Error) + require.NotNil(t, res) + require.Equal(t, string(res.Data), `"baz"`) +} + +func TestQuerierRouteNotFound(t *testing.T) { + t.Parallel() + + env := setupTestEnv() + h := NewHandler(env.keeper) + req := abci.RequestQuery{ + Path: "params/notfound", + Data: []byte{}, + } + res := h.Query(env.ctx, req) + require.Error(t, res.Error) +} From 4a1c0bbbd34ccbe61d94ea19aebc594c7fa576e5 Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Tue, 22 Oct 2024 13:08:10 -0500 Subject: [PATCH 19/24] chore: fixup Signed-off-by: moul <94029+moul@users.noreply.github.com> --- gno.land/cmd/gnoland/testdata/params.txtar | 6 +++--- gnovm/stdlibs/std/params.go | 10 ++++++++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/gno.land/cmd/gnoland/testdata/params.txtar b/gno.land/cmd/gnoland/testdata/params.txtar index 817c616b76d..7f728e14003 100644 --- a/gno.land/cmd/gnoland/testdata/params.txtar +++ b/gno.land/cmd/gnoland/testdata/params.txtar @@ -60,6 +60,6 @@ import ( "std" ) -func SetFoo(newFoo string) { std.SetParam("foo", newFoo) } -func SetBar(newBar bool) { std.SetParam("bar", newBar) } -func SetBaz(newBaz int64) { std.SetParam("baz", newBaz) } +func SetFoo(newFoo string) { std.SetParam("foo.string", newFoo) } +func SetBar(newBar bool) { std.SetParam("bar.bool", newBar) } +func SetBaz(newBaz int64) { std.SetParam("baz.int64", newBaz) } diff --git a/gnovm/stdlibs/std/params.go b/gnovm/stdlibs/std/params.go index f4cd8f7e245..e21bd9912dd 100644 --- a/gnovm/stdlibs/std/params.go +++ b/gnovm/stdlibs/std/params.go @@ -2,6 +2,7 @@ package std import ( "fmt" + "strings" "unicode" gno "github.com/gnolang/gno/gnovm/pkg/gnolang" @@ -47,6 +48,11 @@ func X_setParamBytes(m *gno.Machine, key string, val []byte) { func pkey(m *gno.Machine, key string, kind string) string { // validate key. + untypedKey := strings.TrimSuffix(key, "."+kind) + if key == untypedKey { + m.Panic(typedString("invalid param key: " + key)) + } + if len(key) == 0 { m.Panic(typedString("empty param key")) } @@ -54,7 +60,7 @@ func pkey(m *gno.Machine, key string, kind string) string { if !unicode.IsLetter(first) && first != '_' { m.Panic(typedString("invalid param key: " + key)) } - for _, char := range key[1:] { + for _, char := range untypedKey[1:] { if !unicode.IsLetter(char) && !unicode.IsDigit(char) && char != '_' { m.Panic(typedString("invalid param key: " + key)) } @@ -62,5 +68,5 @@ func pkey(m *gno.Machine, key string, kind string) string { // decorate key with realm and type. _, rlmPath := currentRealm(m) - return fmt.Sprintf("%s.%s.%s", rlmPath, key, kind) + return fmt.Sprintf("%s.%s", rlmPath, key) } From 86b4fbdd961ab2417db5655b7818f00ddd5dff0e Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Tue, 22 Oct 2024 13:12:06 -0500 Subject: [PATCH 20/24] chore: fixup Signed-off-by: moul <94029+moul@users.noreply.github.com> --- tm2/pkg/sdk/params/handler.go | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/tm2/pkg/sdk/params/handler.go b/tm2/pkg/sdk/params/handler.go index 907482db420..b662fc06c58 100644 --- a/tm2/pkg/sdk/params/handler.go +++ b/tm2/pkg/sdk/params/handler.go @@ -64,7 +64,7 @@ func abciResult(err error) sdk.Result { // returns the second component of a path. func secondPart(path string) string { - parts := strings.Split(path, "/") + parts := strings.SplitN(path, "/", 3) if len(parts) < 2 { return "" } else { @@ -74,9 +74,6 @@ func secondPart(path string) string { // returns the third component of a path, including other slashes. func thirdPartWithSlashes(path string) string { - secondSlash := strings.Index(path[strings.Index(path, "/")+1:], "/") - if secondSlash == -1 { - return "" // Return original if less than two slashes - } - return path[strings.Index(path, "/")+secondSlash+2:] + split := strings.SplitN(path, "/", 3) + return split[2] } From 902eb9c49b3f541d82189b175dfc7052a29d0f0d Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Tue, 22 Oct 2024 19:03:08 -0500 Subject: [PATCH 21/24] chore: fixup Signed-off-by: moul <94029+moul@users.noreply.github.com> --- gno.land/pkg/sdk/vm/keeper_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gno.land/pkg/sdk/vm/keeper_test.go b/gno.land/pkg/sdk/vm/keeper_test.go index 9ec74abbf7f..a125e2fb2f0 100644 --- a/gno.land/pkg/sdk/vm/keeper_test.go +++ b/gno.land/pkg/sdk/vm/keeper_test.go @@ -319,12 +319,12 @@ package test import "std" func init() { - std.SetParam("foo", "foo1") + std.SetParam("foo.string", "foo1") } func Do() string { - std.SetParam("bar", int64(1337)) - std.SetParam("foo", "foo2") // override init + std.SetParam("bar.int64", int64(1337)) + std.SetParam("foo.string", "foo2") // override init return "XXX" // return std.GetConfig("gno.land/r/test.foo"), if we want to expose std.GetConfig, maybe as a std.TestGetConfig }`}, From d38c6b8017ac64268a7c2e4e168f91f1b6e57363 Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Tue, 22 Oct 2024 21:25:37 -0500 Subject: [PATCH 22/24] chore: fixup Signed-off-by: moul <94029+moul@users.noreply.github.com> --- gno.land/pkg/sdk/vm/keeper_test.go | 6 +++--- gnovm/stdlibs/std/params.gno | 29 +++++++---------------------- 2 files changed, 10 insertions(+), 25 deletions(-) diff --git a/gno.land/pkg/sdk/vm/keeper_test.go b/gno.land/pkg/sdk/vm/keeper_test.go index a125e2fb2f0..c6d8e3f5fa0 100644 --- a/gno.land/pkg/sdk/vm/keeper_test.go +++ b/gno.land/pkg/sdk/vm/keeper_test.go @@ -319,12 +319,12 @@ package test import "std" func init() { - std.SetParam("foo.string", "foo1") + std.SetParamString("foo.string", "foo1") } func Do() string { - std.SetParam("bar.int64", int64(1337)) - std.SetParam("foo.string", "foo2") // override init + std.SetParamInt64("bar.int64", int64(1337)) + std.SetParamString("foo.string", "foo2") // override init return "XXX" // return std.GetConfig("gno.land/r/test.foo"), if we want to expose std.GetConfig, maybe as a std.TestGetConfig }`}, diff --git a/gnovm/stdlibs/std/params.gno b/gnovm/stdlibs/std/params.gno index febc774c70e..7af4d8a24b3 100644 --- a/gnovm/stdlibs/std/params.gno +++ b/gnovm/stdlibs/std/params.gno @@ -1,30 +1,15 @@ package std -// These are native bindings to the banker's functions. func setParamString(key string, val string) func setParamBool(key string, val bool) func setParamInt64(key string, val int64) func setParamUint64(key string, val uint64) func setParamBytes(key string, val []byte) -// XXX: add doc -func SetParam(key string, val interface{}) { - switch v := val.(type) { - case string: - setParamString(key, v) - case bool: - setParamBool(key, v) - case int64: - setParamInt64(key, v) - case uint64: - setParamUint64(key, v) - case []byte: - setParamBytes(key, v) - default: - panic("unsupported type") - } -} - -//func GetConfig(key string) string { -// return getConfig(key) -//} +var ( + SetParamString = setParamString + SetParamBool = setParamBool + SetParamInt64 = setParamInt64 + SetParamUint64 = setParamUint64 + SetParamBytes = setParamBytes +) From dc37fe0d5f4ef29099211d3f5e2ae9108d431dff Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Tue, 22 Oct 2024 21:29:23 -0500 Subject: [PATCH 23/24] chore: fixup Signed-off-by: moul <94029+moul@users.noreply.github.com> --- gno.land/cmd/gnoland/testdata/params.txtar | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gno.land/cmd/gnoland/testdata/params.txtar b/gno.land/cmd/gnoland/testdata/params.txtar index 7f728e14003..30363aa6369 100644 --- a/gno.land/cmd/gnoland/testdata/params.txtar +++ b/gno.land/cmd/gnoland/testdata/params.txtar @@ -60,6 +60,6 @@ import ( "std" ) -func SetFoo(newFoo string) { std.SetParam("foo.string", newFoo) } -func SetBar(newBar bool) { std.SetParam("bar.bool", newBar) } -func SetBaz(newBaz int64) { std.SetParam("baz.int64", newBaz) } +func SetFoo(newFoo string) { std.SetParamString("foo.string", newFoo) } +func SetBar(newBar bool) { std.SetParamBool("bar.bool", newBar) } +func SetBaz(newBaz int64) { std.SetParamInt64("baz.int64", newBaz) } From 060a3bebf78c96b504e0c1ee5f35a497edad8e99 Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Tue, 22 Oct 2024 23:04:18 -0500 Subject: [PATCH 24/24] chore: fixup Signed-off-by: moul <94029+moul@users.noreply.github.com> --- gnovm/stdlibs/std/params.gno | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/gnovm/stdlibs/std/params.gno b/gnovm/stdlibs/std/params.gno index 7af4d8a24b3..ce400270cda 100644 --- a/gnovm/stdlibs/std/params.gno +++ b/gnovm/stdlibs/std/params.gno @@ -6,10 +6,8 @@ func setParamInt64(key string, val int64) func setParamUint64(key string, val uint64) func setParamBytes(key string, val []byte) -var ( - SetParamString = setParamString - SetParamBool = setParamBool - SetParamInt64 = setParamInt64 - SetParamUint64 = setParamUint64 - SetParamBytes = setParamBytes -) +func SetParamString(key string, val string) { setParamString(key, val) } +func SetParamBool(key string, val bool) { setParamBool(key, val) } +func SetParamInt64(key string, val int64) { setParamInt64(key, val) } +func SetParamUint64(key string, val uint64) { setParamUint64(key, val) } +func SetParamBytes(key string, val []byte) { setParamBytes(key, val) }