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: add 'gnokey maketx run' #1001

Merged
merged 25 commits into from
Nov 23, 2023
Merged
Show file tree
Hide file tree
Changes from 15 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
3 changes: 3 additions & 0 deletions gno.land/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ build.gnofaucet:; go build -o build/gnofaucet ./cmd/gnofaucet
build.gnokey:; go build -o build/gnokey ./cmd/gnokey
build.gnotxsync:; go build -o build/gnotxsync ./cmd/gnotxsync

run.gnoland:; go run ./cmd/gnoland start
run.gnoweb:; go run ./cmd/gnoweb

.PHONY: install
install: install.gnoland install.gnoweb install.gnofaucet install.gnokey install.gnotxsync

Expand Down
11 changes: 4 additions & 7 deletions gno.land/cmd/gnoland/testdata/addpkg.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,14 @@ gnokey maketx addpkg -pkgdir $WORK -pkgpath gno.land/r/foobar/bar -gas-fee 10000
gnokey maketx call -pkgpath gno.land/r/foobar/bar -func Render -gas-fee 1000000ugnot -gas-wanted 2000000 -args '' -broadcast -chainid=tendermint_test test1

## compare render
cmp stdout stdout.golden
stdout '("hello from foo" string)'
stdout 'OK!'
stdout 'GAS WANTED: 2000000'
stdout 'GAS USED: '

-- bar.gno --
package bar

func Render(path string) string {
return "hello from foo"
}

-- stdout.golden --
("hello from foo" string)
OK!
GAS WANTED: 2000000
GAS USED: 69163
28 changes: 28 additions & 0 deletions gno.land/cmd/gnoland/testdata/exec.txtar
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
## start a new node
gnoland start

## add bar.gno package located in $WORK directory as gno.land/r/foobar/bar
gnokey maketx addpkg -pkgdir $WORK/bar -pkgpath gno.land/r/foobar/bar -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1

## execute Render
gnokey maketx run -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1 $WORK/script/script.gno

## compare render
stdout '--- hello from foo ---'
stdout 'OK!'
stdout 'GAS WANTED: 200000'
stdout 'GAS USED: '

-- bar/bar.gno --
package bar

func Render(path string) string {
return "hello from foo"
}

-- script/script.gno --
package main
import "gno.land/r/foobar/bar"
func Main() {
println("---", bar.Render(""), "---")
}
21 changes: 21 additions & 0 deletions gno.land/pkg/sdk/vm/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
return vh.handleMsgAddPackage(ctx, msg)
case MsgCall:
return vh.handleMsgCall(ctx, msg)
case MsgRun:
return vh.handleMsgRun(ctx, msg)

Check warning on line 31 in gno.land/pkg/sdk/vm/handler.go

View check run for this annotation

Codecov / codecov/patch

gno.land/pkg/sdk/vm/handler.go#L30-L31

Added lines #L30 - L31 were not covered by tests
default:
errMsg := fmt.Sprintf("unrecognized vm message type: %T", msg)
return abciResult(std.ErrUnknownRequest(errMsg))
Expand Down Expand Up @@ -77,6 +79,25 @@
*/
}

// Handle MsgRun.
func (vh vmHandler) handleMsgRun(ctx sdk.Context, msg MsgRun) (res sdk.Result) {
amount, err := std.ParseCoins("1000000ugnot") // XXX calculate
if err != nil {
return abciResult(err)
}
err = vh.vm.bank.SendCoins(ctx, msg.Caller, auth.FeeCollectorAddress(), amount)
if err != nil {
return abciResult(err)
}
resstr := ""
resstr, err = vh.vm.Run(ctx, msg)
if err != nil {
return abciResult(err)
}
res.Data = []byte(resstr)
return

Check warning on line 98 in gno.land/pkg/sdk/vm/handler.go

View check run for this annotation

Codecov / codecov/patch

gno.land/pkg/sdk/vm/handler.go#L83-L98

Added lines #L83 - L98 were not covered by tests
}

//----------------------------------------
// Query

Expand Down
85 changes: 85 additions & 0 deletions gno.land/pkg/sdk/vm/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// TODO: move most of the logic in ROOT/gno.land/...

import (
"bytes"
"fmt"
"os"
"strings"
Expand All @@ -27,6 +28,7 @@
type VMKeeperI interface {
AddPackage(ctx sdk.Context, msg MsgAddPackage) error
Call(ctx sdk.Context, msg MsgCall) (res string, err error)
Run(ctx sdk.Context, msg MsgRun) (res string, err error)
}

var _ VMKeeperI = &VMKeeper{}
Expand Down Expand Up @@ -282,6 +284,89 @@
// TODO pay for gas? TODO see context?
}

// Run executes arbitrary Gno code in the context of the caller's realm.
func (vm *VMKeeper) Run(ctx sdk.Context, msg MsgRun) (res string, err error) {
caller := msg.Caller
pkgAddr := caller
store := vm.getGnoStore(ctx)
send := msg.Send
memPkg := msg.Package

// Validate arguments.
callerAcc := vm.acck.GetAccount(ctx, caller)
if callerAcc == nil {
return "", std.ErrUnknownAddress(fmt.Sprintf("account %s does not exist", caller))
}

Check warning on line 299 in gno.land/pkg/sdk/vm/keeper.go

View check run for this annotation

Codecov / codecov/patch

gno.land/pkg/sdk/vm/keeper.go#L298-L299

Added lines #L298 - L299 were not covered by tests
if err := msg.Package.Validate(); err != nil {
return "", ErrInvalidPkgPath(err.Error())
}

Check warning on line 302 in gno.land/pkg/sdk/vm/keeper.go

View check run for this annotation

Codecov / codecov/patch

gno.land/pkg/sdk/vm/keeper.go#L301-L302

Added lines #L301 - L302 were not covered by tests

// Send send-coins to pkg from caller.
err = vm.bank.SendCoins(ctx, caller, pkgAddr, send)
if err != nil {
return "", err
}

Check warning on line 308 in gno.land/pkg/sdk/vm/keeper.go

View check run for this annotation

Codecov / codecov/patch

gno.land/pkg/sdk/vm/keeper.go#L307-L308

Added lines #L307 - L308 were not covered by tests

// Parse and run the files, construct *PV.
msgCtx := stdlibs.ExecContext{
ChainID: ctx.ChainID(),
Height: ctx.BlockHeight(),
Timestamp: ctx.BlockTime().Unix(),
Msg: msg,
OrigCaller: caller.Bech32(),
OrigSend: send,
OrigSendSpent: new(std.Coins),
OrigPkgAddr: pkgAddr.Bech32(),
Banker: NewSDKBanker(vm, ctx),
}
// Parse and run the files, construct *PV.
buf := new(bytes.Buffer)
m := gno.NewMachineWithOptions(
gno.MachineOptions{
PkgPath: "",
moul marked this conversation as resolved.
Show resolved Hide resolved
Output: buf,
Store: store,
Alloc: store.GetAllocator(),
Context: msgCtx,
MaxCycles: vm.maxCycles,
})
defer m.Release()
// memPkg.Path = "gno.land/r/main"
_, pv := m.RunMemPackage(memPkg, true) // XXX: just save the state, not the contract

ctx.Logger().Info("CPUCYCLES", "addpkg", m.Cycles)

expr := fmt.Sprintf(`pkg.Main()`)
xn := gno.MustParseExpr(expr)
mpn := gno.NewPackageNode("main", "main", nil)
mpn.Define("pkg", gno.TypedValue{T: &gno.PackageType{}, V: pv})
mpv := mpn.NewPackage()

m2 := gno.NewMachineWithOptions(
gno.MachineOptions{
PkgPath: "",
Output: buf,
Store: store,
Alloc: store.GetAllocator(),
Context: msgCtx,
MaxCycles: vm.maxCycles,
})

m2.SetActivePackage(mpv)
defer func() {
if r := recover(); r != nil {
err = errors.Wrap(fmt.Errorf("%v", r), "VM call panic: %v\n%s\n",
r, m2.String())
return
}

Check warning on line 361 in gno.land/pkg/sdk/vm/keeper.go

View check run for this annotation

Codecov / codecov/patch

gno.land/pkg/sdk/vm/keeper.go#L358-L361

Added lines #L358 - L361 were not covered by tests
m2.Release()
}()
m2.Eval(xn)
ctx.Logger().Info("CPUCYCLES call: ", m2.Cycles)
res = buf.String()
return res, nil
}

// QueryFuncs returns public facing function signatures.
func (vm *VMKeeper) QueryFuncs(ctx sdk.Context, pkgPath string) (fsigs FunctionSignatures, err error) {
store := vm.getGnoStore(ctx)
Expand Down
58 changes: 58 additions & 0 deletions gno.land/pkg/sdk/vm/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -337,3 +337,61 @@ func GetAdmin() string {
assert.NoError(t, err)
assert.Equal(t, res, addrString)
}

// Call Run without imports, without variables.
func TestVMKeeperRunSimple(t *testing.T) {
env := setupTestEnv()
ctx := env.ctx

// Give "addr1" some gnots.
addr := crypto.AddressFromPreimage([]byte("addr1"))
acc := env.acck.NewAccountWithAddress(ctx, addr)
env.acck.SetAccount(ctx, acc)

files := []*std.MemFile{
{"script.gno", `
package main

func Main() {
println("hello world!")
}
`},
}

coins := std.MustParseCoins("")
msg2 := NewMsgRun(addr, coins, files)
res, err := env.vmk.Run(ctx, msg2)
assert.NoError(t, err)
assert.Equal(t, res, "hello world!\n")
}

// Call Run with stdlibs.
func TestVMKeeperRunImportStdlibs(t *testing.T) {
env := setupTestEnv()
ctx := env.ctx

// Give "addr1" some gnots.
addr := crypto.AddressFromPreimage([]byte("addr1"))
acc := env.acck.NewAccountWithAddress(ctx, addr)
env.acck.SetAccount(ctx, acc)

files := []*std.MemFile{
{"script.gno", `
package main

import "std"

func Main() {
addr := std.GetOrigCaller()
println("hello world!", addr)
}
`},
}

coins := std.MustParseCoins("")
msg2 := NewMsgRun(addr, coins, files)
res, err := env.vmk.Run(ctx, msg2)
assert.NoError(t, err)
expectedString := fmt.Sprintf("hello world! %s\n", addr.String())
assert.Equal(t, res, expectedString)
}
64 changes: 64 additions & 0 deletions gno.land/pkg/sdk/vm/msgs.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,3 +135,67 @@
func (msg MsgCall) GetReceived() std.Coins {
return msg.Send
}

//----------------------------------------
// MsgRun

// MsgRun - executes arbitrary Gno code.
type MsgRun struct {
Caller crypto.Address `json:"caller" yaml:"caller"`
Send std.Coins `json:"send" yaml:"send"`
Package *std.MemPackage `json:"package" yaml:"package"`
}

var _ std.Msg = MsgRun{}

func NewMsgRun(caller crypto.Address, send std.Coins, files []*std.MemFile) MsgRun {
for _, file := range files {
if strings.HasSuffix(file.Name, ".gno") {
pkgName := string(gno.PackageNameFromFileBody(file.Name, file.Body))
if pkgName != "main" {
panic("package name should be 'main'")

Check warning on line 156 in gno.land/pkg/sdk/vm/msgs.go

View check run for this annotation

Codecov / codecov/patch

gno.land/pkg/sdk/vm/msgs.go#L156

Added line #L156 was not covered by tests
}
}
}
return MsgRun{
Caller: caller,
Send: send,
Package: &std.MemPackage{
Name: "main",
Path: "gno.land/r/main", // XXX
Files: files,
},
}
}

// Implements Msg.
func (msg MsgRun) Route() string { return RouterKey }

Check warning on line 172 in gno.land/pkg/sdk/vm/msgs.go

View check run for this annotation

Codecov / codecov/patch

gno.land/pkg/sdk/vm/msgs.go#L172

Added line #L172 was not covered by tests

// Implements Msg.
func (msg MsgRun) Type() string { return "run" }

Check warning on line 175 in gno.land/pkg/sdk/vm/msgs.go

View check run for this annotation

Codecov / codecov/patch

gno.land/pkg/sdk/vm/msgs.go#L175

Added line #L175 was not covered by tests

// Implements Msg.
func (msg MsgRun) ValidateBasic() error {
if msg.Caller.IsZero() {
return std.ErrInvalidAddress("missing caller address")
}
if msg.Package.Path == "" { // XXX
return ErrInvalidPkgPath("missing package path")
}
return nil

Check warning on line 185 in gno.land/pkg/sdk/vm/msgs.go

View check run for this annotation

Codecov / codecov/patch

gno.land/pkg/sdk/vm/msgs.go#L178-L185

Added lines #L178 - L185 were not covered by tests
}

// Implements Msg.
func (msg MsgRun) GetSignBytes() []byte {
return std.MustSortJSON(amino.MustMarshalJSON(msg))

Check warning on line 190 in gno.land/pkg/sdk/vm/msgs.go

View check run for this annotation

Codecov / codecov/patch

gno.land/pkg/sdk/vm/msgs.go#L189-L190

Added lines #L189 - L190 were not covered by tests
}

// Implements Msg.
func (msg MsgRun) GetSigners() []crypto.Address {
return []crypto.Address{msg.Caller}

Check warning on line 195 in gno.land/pkg/sdk/vm/msgs.go

View check run for this annotation

Codecov / codecov/patch

gno.land/pkg/sdk/vm/msgs.go#L194-L195

Added lines #L194 - L195 were not covered by tests
}

// Implements ReceiveMsg.
func (msg MsgRun) GetReceived() std.Coins {
return msg.Send

Check warning on line 200 in gno.land/pkg/sdk/vm/msgs.go

View check run for this annotation

Codecov / codecov/patch

gno.land/pkg/sdk/vm/msgs.go#L199-L200

Added lines #L199 - L200 were not covered by tests
}
1 change: 1 addition & 0 deletions gno.land/pkg/sdk/vm/package.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ var Package = amino.RegisterPackage(amino.NewPackage(
std.Package,
).WithTypes(
MsgCall{}, "m_call",
MsgRun{}, "m_run",
MsgAddPackage{}, "m_addpkg", // TODO rename both to MsgAddPkg?

// errors
Expand Down
1 change: 1 addition & 0 deletions tm2/pkg/crypto/keys/client/maketx.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
newAddPkgCmd(cfg, io),
newSendCmd(cfg, io),
newCallCmd(cfg, io),
newRunCmd(cfg, io),

Check warning on line 39 in tm2/pkg/crypto/keys/client/maketx.go

View check run for this annotation

Codecov / codecov/patch

tm2/pkg/crypto/keys/client/maketx.go#L39

Added line #L39 was not covered by tests
)

return cmd
Expand Down
Loading
Loading