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

chore(gnoclient): Add Run support #1574

Merged
merged 2 commits into from
Jan 25, 2024
Merged
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
48 changes: 48 additions & 0 deletions gno.land/pkg/gnoclient/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
rpcclient "github.com/gnolang/gno/tm2/pkg/bft/rpc/client"
"github.com/gnolang/gno/tm2/pkg/crypto/keys"
"github.com/gnolang/gno/tm2/pkg/log"
"github.com/gnolang/gno/tm2/pkg/std"
"github.com/jaekwon/testify/require"
)

Expand Down Expand Up @@ -50,3 +51,50 @@ func TestClient_Request(t *testing.T) {

// XXX: need more test
}

func TestClient_Run(t *testing.T) {
config, _ := integration.TestingNodeConfig(t, gnoenv.RootDir())
node, remoteAddr := integration.TestingInMemoryNode(t, log.NewNopLogger(), config)
defer node.Stop()

signer := newInMemorySigner(t, config.TMConfig.ChainID())

client := Client{
Signer: signer,
RPCClient: rpcclient.NewHTTP(remoteAddr, "/websocket"),
}

code := `package main

import (
"std"

"gno.land/p/demo/ufmt"
"gno.land/r/demo/tests"
)

func main() {
println(ufmt.Sprintf("- before: %d", tests.Counter()))
for i := 0; i < 10; i++ {
tests.IncCounter()
}
println(ufmt.Sprintf("- after: %d", tests.Counter()))
}`
memPkg := &std.MemPackage{
Files: []*std.MemFile{
{
Name: "main.gno",
Body: code,
},
},
}
res, err := client.Run(RunCfg{
Package: memPkg,
GasFee: "1ugnot",
GasWanted: 100000000,
})
require.NoError(t, err)
require.NotNil(t, res)
require.NotEmpty(t, res.DeliverTx.Data)
require.Equal(t, string(res.DeliverTx.Data), "- before: 0\n- after: 10\n")
}
66 changes: 66 additions & 0 deletions gno.land/pkg/gnoclient/client_txs.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import (
"github.com/gnolang/gno/gno.land/pkg/sdk/vm"
gno "github.com/gnolang/gno/gnovm/pkg/gnolang"
"github.com/gnolang/gno/tm2/pkg/amino"
ctypes "github.com/gnolang/gno/tm2/pkg/bft/rpc/core/types"
"github.com/gnolang/gno/tm2/pkg/errors"
Expand All @@ -21,6 +22,16 @@
Memo string // Memo
}

// RunCfg contains configuration options for running a temporary package on the blockchain.
type RunCfg struct {
Package *std.MemPackage
GasFee string // Gas fee
GasWanted int64 // Gas wanted
AccountNumber uint64 // Account number
SequenceNumber uint64 // Sequence number
Memo string // Memo
}

// Call executes a contract call on the blockchain.
func (c *Client) Call(cfg CallCfg) (*ctypes.ResultBroadcastTxCommit, error) {
// Validate required client fields.
Expand Down Expand Up @@ -81,6 +92,61 @@
return c.signAndBroadcastTxCommit(tx, accountNumber, sequenceNumber)
}

// Temporarily load cfg.Package on the blockchain and run main() which can
// call realm functions and use println() to output to the "console".
// This returns bres where string(bres.DeliverTx.Data) is the "console" output.
func (c *Client) Run(cfg RunCfg) (*ctypes.ResultBroadcastTxCommit, error) {
// Validate required client fields.
if err := c.validateSigner(); err != nil {
return nil, errors.Wrap(err, "validate signer")
}

Check warning on line 102 in gno.land/pkg/gnoclient/client_txs.go

View check run for this annotation

Codecov / codecov/patch

gno.land/pkg/gnoclient/client_txs.go#L101-L102

Added lines #L101 - L102 were not covered by tests
if err := c.validateRPCClient(); err != nil {
return nil, errors.Wrap(err, "validate RPC client")
}

Check warning on line 105 in gno.land/pkg/gnoclient/client_txs.go

View check run for this annotation

Codecov / codecov/patch

gno.land/pkg/gnoclient/client_txs.go#L104-L105

Added lines #L104 - L105 were not covered by tests

memPkg := cfg.Package
gasWanted := cfg.GasWanted
gasFee := cfg.GasFee
sequenceNumber := cfg.SequenceNumber
accountNumber := cfg.AccountNumber
memo := cfg.Memo

// Validate config.
if memPkg.IsEmpty() {
return nil, errors.New("found an empty package " + memPkg.Path)
}

Check warning on line 117 in gno.land/pkg/gnoclient/client_txs.go

View check run for this annotation

Codecov / codecov/patch

gno.land/pkg/gnoclient/client_txs.go#L116-L117

Added lines #L116 - L117 were not covered by tests

// Parse gas wanted & fee.
gasFeeCoins, err := std.ParseCoin(gasFee)
if err != nil {
return nil, errors.Wrap(err, "parsing gas fee coin")
}

Check warning on line 123 in gno.land/pkg/gnoclient/client_txs.go

View check run for this annotation

Codecov / codecov/patch

gno.land/pkg/gnoclient/client_txs.go#L122-L123

Added lines #L122 - L123 were not covered by tests

caller := c.Signer.Info().GetAddress()

// precompile and validate syntax
err = gno.PrecompileAndCheckMempkg(memPkg)
if err != nil {
return nil, errors.Wrap(err, "precompile and check")
}

Check warning on line 131 in gno.land/pkg/gnoclient/client_txs.go

View check run for this annotation

Codecov / codecov/patch

gno.land/pkg/gnoclient/client_txs.go#L130-L131

Added lines #L130 - L131 were not covered by tests
memPkg.Name = "main"
memPkg.Path = "gno.land/r/" + caller.String() + "/run"

// Construct message & transaction and marshal.
msg := vm.MsgRun{
Caller: caller,
Package: memPkg,
}
tx := std.Tx{
Msgs: []std.Msg{msg},
Fee: std.NewFee(gasWanted, gasFeeCoins),
Signatures: nil,
Memo: memo,
}

return c.signAndBroadcastTxCommit(tx, accountNumber, sequenceNumber)
}

// signAndBroadcastTxCommit signs a transaction and broadcasts it, returning the result.
func (c Client) signAndBroadcastTxCommit(tx std.Tx, accountNumber, sequenceNumber uint64) (*ctypes.ResultBroadcastTxCommit, error) {
caller := c.Signer.Info().GetAddress()
Expand Down
Loading