Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat(keeper): implement json primitive return #2949

Open
wants to merge 23 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions gno.land/pkg/gnoland/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -430,11 +430,9 @@ func EndBlocker(
}

// Run the VM to get the updates from the chain
response, err := vmk.QueryEval(
ctx,
valRealm,
fmt.Sprintf("%s(%d)", valChangesFn, app.LastBlockHeight()),
)
expr := fmt.Sprintf("%s(%d)", valChangesFn, app.LastBlockHeight())
msgEval := vm.NewMsgEval(vm.FormatDefault, valRealm, expr)
Copy link
Member

Choose a reason for hiding this comment

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

@zivkovicmilos, we don't need to port this module in this PR, but I'd like your opinion on what the diff will look like thanks to the new JSON built-in support.

response, err := vmk.Eval(ctx, msgEval)
if err != nil {
app.Logger().Error("unable to call VM during EndBlocker", "err", err)

Expand Down
18 changes: 9 additions & 9 deletions gno.land/pkg/gnoland/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -517,11 +517,11 @@ func TestEndBlocker(t *testing.T) {
mockEventSwitch = newCommonEvSwitch()

mockVMKeeper = &mockVMKeeper{
queryFn: func(_ sdk.Context, pkgPath, expr string) (string, error) {
evalFn: func(ctx sdk.Context, msg vm.MsgEval) (string, error) {
vmCalled = true

require.Equal(t, valRealm, pkgPath)
require.NotEmpty(t, expr)
require.Equal(t, valRealm, msg.PkgPath)
require.NotEmpty(t, msg.Expr)

return "", errors.New("random call error")
},
Expand Down Expand Up @@ -560,11 +560,11 @@ func TestEndBlocker(t *testing.T) {
mockEventSwitch = newCommonEvSwitch()

mockVMKeeper = &mockVMKeeper{
queryFn: func(_ sdk.Context, pkgPath, expr string) (string, error) {
evalFn: func(ctx sdk.Context, msg vm.MsgEval) (string, error) {
vmCalled = true

require.Equal(t, valRealm, pkgPath)
require.NotEmpty(t, expr)
require.Equal(t, valRealm, msg.PkgPath)
require.NotEmpty(t, msg.Expr)

return constructVMResponse([]abci.ValidatorUpdate{}), nil
},
Expand Down Expand Up @@ -599,9 +599,9 @@ func TestEndBlocker(t *testing.T) {
mockEventSwitch = newCommonEvSwitch()

mockVMKeeper = &mockVMKeeper{
queryFn: func(_ sdk.Context, pkgPath, expr string) (string, error) {
require.Equal(t, valRealm, pkgPath)
require.NotEmpty(t, expr)
evalFn: func(ctx sdk.Context, msg vm.MsgEval) (string, error) {
require.Equal(t, valRealm, msg.PkgPath)
require.NotEmpty(t, msg.Expr)

return constructVMResponse(changes), nil
},
Expand Down
9 changes: 9 additions & 0 deletions gno.land/pkg/gnoland/mock_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ func (m *mockEventSwitch) RemoveListener(listenerID string) {
type mockVMKeeper struct {
addPackageFn func(sdk.Context, vm.MsgAddPackage) error
callFn func(sdk.Context, vm.MsgCall) (string, error)
evalFn func(sdk.Context, vm.MsgEval) (string, error)
queryFn func(sdk.Context, string, string) (string, error)
runFn func(sdk.Context, vm.MsgRun) (string, error)
loadStdlibFn func(sdk.Context, string)
Expand All @@ -72,6 +73,14 @@ func (m *mockVMKeeper) Call(ctx sdk.Context, msg vm.MsgCall) (res string, err er
return "", nil
}

func (m *mockVMKeeper) Eval(ctx sdk.Context, msg vm.MsgEval) (res string, err error) {
if m.evalFn != nil {
return m.evalFn(ctx, msg)
}

return "", nil
}

func (m *mockVMKeeper) QueryEval(ctx sdk.Context, pkgPath, expr string) (res string, err error) {
if m.queryFn != nil {
return m.queryFn(ctx, pkgPath, expr)
Expand Down
63 changes: 63 additions & 0 deletions gno.land/pkg/integration/testdata/gnokey.txtar
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
loadpkg gno.land/r/dev/silo $WORK/silo

# test basic gnokey integrations commands
# golden files have been generated using UPDATE_SCRIPTS=true

Expand All @@ -7,6 +9,9 @@ adduser user1
# start gnoland
gnoland start

# query

## auth
## test1 account should be available on default
gnokey query auth/accounts/$user1_user_addr
stdout 'height: 0'
Expand All @@ -25,3 +30,61 @@ stdout '}'
! gnokey query foo/bar
stdout 'Log:'
stderr '"gnokey" error: unknown request error'

# jquery

## succeed call: string
gnokey jquery vm/qeval -data "gno.land/r/dev/silo.Hello(\"juliette\")"
stdout '"returns": \['
stdout ' "Hello juliette",'
stdout ' null'
stdout '\]'

## succeed call: error
gnokey jquery vm/qeval -data "gno.land/r/dev/silo.Hello(\"bernard\")"
stdout '"returns": \['
stdout ' "",'
stdout ' "not for you bernard!"'
stdout '\]'

## success call: object
gnokey jquery vm/qeval -data "gno.land/r/dev/silo.GetSilo()"
stdout '"returns": \['
stdout ' "<obj:\*gno.land/r/dev/silo.Silo:6aed2eda72c79e411eec1ea7f661c5e30383fb59:2>"'
stdout '\]'

## success call: []byte
gnokey jquery vm/qeval -data "gno.land/r/dev/silo.GetSiloData()"
stdout '"returns": \['
stdout ' "c2VjcmV0IG1lc3NhZ2U="'
stdout '\]'


-- silo/silo.gno --
package silo

import "errors"

type Silo struct {
Data string
}

var mysilo = Silo{
Data: "secret message",
}

func Hello(name string) (string, error) {
if name == "bernard" {
return "", errors.New("not for you bernard!")
}

return "Hello "+name, nil
}

func GetSilo() *Silo {
return &mysilo
}

func GetSiloData() []byte {
return []byte(mysilo.Data)
}
84 changes: 84 additions & 0 deletions gno.land/pkg/keyscli/query_json.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package keyscli

import (
"bytes"
"context"
"encoding/json"
"flag"
"fmt"

"github.com/gnolang/gno/tm2/pkg/amino"
"github.com/gnolang/gno/tm2/pkg/commands"
"github.com/gnolang/gno/tm2/pkg/crypto/keys/client"
)

func NewJSONQueryCmd(rootCfg *client.BaseCfg, io commands.IO) *commands.Command {
cfg := &client.QueryCfg{
RootCfg: rootCfg,
}

return commands.NewCommand(
commands.Metadata{
Name: "jquery",
Copy link
Member

Choose a reason for hiding this comment

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

This name gives me shivers. Could we not do gnokey query --json? Seems also easier to support in broadcast and maketx if it's just a flag.

Copy link
Member

Choose a reason for hiding this comment

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

What case do you see with broadcast/maketx and JSON?

I personally like "jquery"; it sounds like "anti-web 2.0." :)

ShortUsage: "jquery [flags] <path>",
ShortHelp: "EXPERIMENTAL: makes an ABCI query and return a result in json",
LongHelp: "EXPERIMENTAL: makes an ABCI query and return a result in json",
},
cfg,
func(_ context.Context, args []string) error {
return execJSONQuery(cfg, args, io)
},
)
}

func execJSONQuery(cfg *client.QueryCfg, args []string, io commands.IO) error {
if len(args) != 1 {
return flag.ErrHelp
}

Check warning on line 37 in gno.land/pkg/keyscli/query_json.go

View check run for this annotation

Codecov / codecov/patch

gno.land/pkg/keyscli/query_json.go#L36-L37

Added lines #L36 - L37 were not covered by tests

cfg.Path = args[0]
if cfg.Path == "vm/qeval" {
// automatically add json suffix for qeval
cfg.Path = cfg.Path + "/json"
Copy link
Member

Choose a reason for hiding this comment

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

looks weird. what's the goal? maybe add a comment if there is a valid reason.

Copy link
Member Author

Choose a reason for hiding this comment

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

Will do. The reason for that is this jquery is made to handle JSON, so you want your query to return JSON. Therefore, you should not have to specify the format for the query. The goal is to automatically pass the argument to produce a JSON output without having to specify it every time.
query vm/qeval abc.xy/r/toto -> machine
jquery vm/qeval abc.xy/r/toto -> JSON

}

qres, err := client.QueryHandler(cfg)
if err != nil {
return err
}

Check warning on line 48 in gno.land/pkg/keyscli/query_json.go

View check run for this annotation

Codecov / codecov/patch

gno.land/pkg/keyscli/query_json.go#L47-L48

Added lines #L47 - L48 were not covered by tests

var output struct {
Response json.RawMessage `json:"response"`
Returns json.RawMessage `json:"returns,omitempty"`
}

if output.Response, err = amino.MarshalJSONIndent(qres.Response, "", " "); err != nil {
io.ErrPrintfln("Unable to marshal response %+v\n", qres)
return fmt.Errorf("amino marshal json error: %w", err)
}

Check warning on line 58 in gno.land/pkg/keyscli/query_json.go

View check run for this annotation

Codecov / codecov/patch

gno.land/pkg/keyscli/query_json.go#L56-L58

Added lines #L56 - L58 were not covered by tests

// XXX: this is probably too specific
if cfg.Path == "vm/qeval/json" {
Copy link
Member

Choose a reason for hiding this comment

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

Shouldn't this be a preliminary check raising an error at the beginning of the function?

if len(qres.Response.Data) > 0 {
output.Returns = qres.Response.Data
} else {
output.Returns = []byte("[]")
}

Check warning on line 66 in gno.land/pkg/keyscli/query_json.go

View check run for this annotation

Codecov / codecov/patch

gno.land/pkg/keyscli/query_json.go#L65-L66

Added lines #L65 - L66 were not covered by tests
}

var buff bytes.Buffer
jqueryEnc := json.NewEncoder(&buff)
jqueryEnc.SetEscapeHTML(false) // disable HTML escaping, as we want to correctly display `<`, `>`
jqueryEnc.SetIndent("", " ")

if err := jqueryEnc.Encode(output); err != nil {
io.ErrPrintfln("Unable to marshal\n Response: %+v\n Returns: %+v\n",
string(output.Response),
string(output.Returns),
)
return fmt.Errorf("marshal json error: %w", err)
}

Check warning on line 80 in gno.land/pkg/keyscli/query_json.go

View check run for this annotation

Codecov / codecov/patch

gno.land/pkg/keyscli/query_json.go#L75-L80

Added lines #L75 - L80 were not covered by tests

io.Println(buff.String())
return nil
}
3 changes: 3 additions & 0 deletions gno.land/pkg/keyscli/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ func NewRootCmd(io commands.IO, base client.BaseOptions) *commands.Command {
client.NewQueryCmd(cfg, io),
client.NewBroadcastCmd(cfg, io),

// Custom query command
NewJSONQueryCmd(cfg, io),

// Custom MakeTX command
NewMakeTxCmd(cfg, io),
)
Expand Down
Loading
Loading