Skip to content

Commit

Permalink
feat: add 'gnokey maketx run' (gnolang#1001)
Browse files Browse the repository at this point in the history
  • Loading branch information
moul authored and gfanton committed Jan 18, 2024
1 parent 36eabae commit a7d00d9
Show file tree
Hide file tree
Showing 10 changed files with 402 additions and 3 deletions.
3 changes: 3 additions & 0 deletions gno.land/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ build.gnokey:; go build -o build/gnokey ./cmd/gnokey
build.gnotxsync:; go build -o build/gnotxsync ./cmd/gnotxsync
build.genesis:; go build -o build/genesis ./cmd/genesis

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 install.genesis

Expand Down
4 changes: 1 addition & 3 deletions gno.land/cmd/gnoland/testdata/addpkg.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,9 @@ stdout 'OK!'
stdout 'GAS WANTED: 2000000'
stdout 'GAS USED: [0-9]+'


-- bar.gno --
package bar

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

}
28 changes: 28 additions & 0 deletions gno.land/cmd/gnoland/testdata/run.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 'main: --- 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("main: ---", 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 @@ func (vh vmHandler) Process(ctx sdk.Context, msg std.Msg) sdk.Result {
return vh.handleMsgAddPackage(ctx, msg)
case MsgCall:
return vh.handleMsgCall(ctx, msg)
case MsgRun:
return vh.handleMsgRun(ctx, msg)
default:
errMsg := fmt.Sprintf("unrecognized vm message type: %T", msg)
return abciResult(std.ErrUnknownRequest(errMsg))
Expand Down Expand Up @@ -77,6 +79,25 @@ func (vh vmHandler) handleMsgCall(ctx sdk.Context, msg MsgCall) (res sdk.Result)
*/
}

// 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
}

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

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

import (
"bytes"
"fmt"
"os"
"regexp"
"strings"

gno "github.com/gnolang/gno/gnovm/pkg/gnolang"
Expand All @@ -27,6 +29,7 @@ const (
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 @@ -128,6 +131,10 @@ func (vm *VMKeeper) getGnoStore(ctx sdk.Context) gno.Store {
}
}

const (
reReservedPath = `gno\.land/r/g[a-z0-9]+/run`
)

// AddPackage adds a package with given fileset.
func (vm *VMKeeper) AddPackage(ctx sdk.Context, msg MsgAddPackage) error {
creator := msg.Creator
Expand All @@ -150,6 +157,11 @@ func (vm *VMKeeper) AddPackage(ctx sdk.Context, msg MsgAddPackage) error {
if pv := store.GetPackage(pkgPath, false); pv != nil {
return ErrInvalidPkgPath("package already exists: " + pkgPath)
}

if ok, _ := regexp.MatchString(reReservedPath, pkgPath); ok {
return ErrInvalidPkgPath("reserved package name: " + pkgPath)
}

// Pay deposit from creator.
pkgAddr := gno.DerivePkgAddr(pkgPath)

Expand Down Expand Up @@ -282,6 +294,80 @@ func (vm *VMKeeper) Call(ctx sdk.Context, msg MsgCall) (res string, err error) {
// 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))
}
if err := msg.Package.Validate(); err != nil {
return "", ErrInvalidPkgPath(err.Error())
}

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

// 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: "",
Output: buf,
Store: store,
Alloc: store.GetAllocator(),
Context: msgCtx,
MaxCycles: vm.maxCycles,
})
defer m.Release()
_, pv := m.RunMemPackage(memPkg, false)
ctx.Logger().Info("CPUCYCLES", "addpkg", m.Cycles)

m2 := gno.NewMachineWithOptions(
gno.MachineOptions{
PkgPath: "",
Output: buf,
Store: store,
Alloc: store.GetAllocator(),
Context: msgCtx,
MaxCycles: vm.maxCycles,
})
m2.SetActivePackage(pv)
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
}
m2.Release()
}()
m2.RunMain()
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) GetSigners() []crypto.Address {
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'")
}
}
}
return MsgRun{
Caller: caller,
Send: send,
Package: &std.MemPackage{
Name: "main",
Path: "gno.land/r/" + caller.String() + "/run",
Files: files,
},
}
}

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

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

// 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
}

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

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

// Implements ReceiveMsg.
func (msg MsgRun) GetReceived() std.Coins {
return msg.Send
}
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 @@ func newMakeTxCmd(rootCfg *baseCfg, io commands.IO) *commands.Command {
newAddPkgCmd(cfg, io),
newSendCmd(cfg, io),
newCallCmd(cfg, io),
newRunCmd(cfg, io),
)

return cmd
Expand Down
Loading

0 comments on commit a7d00d9

Please sign in to comment.