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): modify Run to use baseTxCfg & multiple MsgRun args #1637

Merged
merged 13 commits into from
Feb 23, 2024
186 changes: 186 additions & 0 deletions gno.land/pkg/gnoclient/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ func TestClient_Render(t *testing.T) {
assert.Equal(t, data.Response.Data, expectedRender)
}

// Call tests
func TestClient_CallSingle(t *testing.T) {
t.Parallel()

Expand Down Expand Up @@ -359,3 +360,188 @@ func TestClient_Call_Errors(t *testing.T) {
})
}
}

// Run tests
func TestClient_Run_Errors(t *testing.T) {
thehowl marked this conversation as resolved.
Show resolved Hide resolved
t.Parallel()

testCases := []struct {
name string
client Client
cfg BaseTxCfg
msgs []MsgRun
expectedError error
}{
{
name: "Invalid Signer",
client: Client{
Signer: nil,
RPCClient: &mockRPCClient{},
},
cfg: BaseTxCfg{
GasWanted: 100000,
GasFee: "10000ugnot",
AccountNumber: 1,
SequenceNumber: 1,
Memo: "Test memo",
},
msgs: []MsgRun{
{
Package: &std.MemPackage{
Name: "",
Path: "",
Files: []*std.MemFile{
{
Name: "file1.gno",
Body: "",
},
},
},
Send: "",
},
},
expectedError: ErrMissingSigner,
},
{
name: "Invalid RPCClient",
client: Client{
&mockSigner{},
nil,
},
cfg: BaseTxCfg{
GasWanted: 100000,
GasFee: "10000ugnot",
AccountNumber: 1,
SequenceNumber: 1,
Memo: "Test memo",
},
msgs: []MsgRun{},
expectedError: ErrMissingRPCClient,
},
{
name: "Invalid Gas Fee",
client: Client{
Signer: &mockSigner{},
RPCClient: &mockRPCClient{},
},
cfg: BaseTxCfg{
GasWanted: 100000,
GasFee: "",
AccountNumber: 1,
SequenceNumber: 1,
Memo: "Test memo",
},
msgs: []MsgRun{
{
Package: &std.MemPackage{
Name: "",
Path: "",
Files: []*std.MemFile{
{
Name: "file1.gno",
Body: "",
},
},
},
Send: "",
},
},
expectedError: ErrInvalidGasFee,
},
{
name: "Negative Gas Wanted",
client: Client{
Signer: &mockSigner{},
RPCClient: &mockRPCClient{},
},
cfg: BaseTxCfg{
GasWanted: -1,
GasFee: "10000ugnot",
AccountNumber: 1,
SequenceNumber: 1,
Memo: "Test memo",
},
msgs: []MsgRun{
{
Package: &std.MemPackage{
Name: "",
Path: "",
Files: []*std.MemFile{
{
Name: "file1.gno",
Body: "",
},
},
},
Send: "",
},
},
expectedError: ErrInvalidGasWanted,
},
{
name: "0 Gas Wanted",
client: Client{
Signer: &mockSigner{},
RPCClient: &mockRPCClient{},
},
cfg: BaseTxCfg{
GasWanted: 0,
GasFee: "10000ugnot",
AccountNumber: 1,
SequenceNumber: 1,
Memo: "Test memo",
},
msgs: []MsgRun{
{
Package: &std.MemPackage{
Name: "",
Path: "",
Files: []*std.MemFile{
{
Name: "file1.gno",
Body: "",
},
},
},
Send: "",
},
},
expectedError: ErrInvalidGasWanted,
},
{
name: "Invalid Empty Package",
client: Client{
Signer: &mockSigner{},
RPCClient: &mockRPCClient{},
},
cfg: BaseTxCfg{
GasWanted: 100000,
GasFee: "10000ugnot",
AccountNumber: 1,
SequenceNumber: 1,
Memo: "Test memo",
},
msgs: []MsgRun{
{
Package: &std.MemPackage{
Name: "",
Path: "",
Files: []*std.MemFile{},
},
Send: "",
},
},
expectedError: ErrEmptyPackage,
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()

res, err := tc.client.Run(tc.cfg, tc.msgs...)
assert.Nil(t, res)
assert.ErrorIs(t, err, tc.expectedError)
})
}
}
98 changes: 54 additions & 44 deletions gno.land/pkg/gnoclient/client_txs.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
var (
ErrEmptyPkgPath = errors.New("empty pkg path")
ErrEmptyFuncName = errors.New("empty function name")
ErrEmptyPackage = errors.New("empty package to run")
ErrInvalidGasWanted = errors.New("invalid gas wanted")
ErrInvalidGasFee = errors.New("invalid gas fee")
ErrMissingSigner = errors.New("missing Signer")
Expand All @@ -34,14 +35,9 @@
Send string // Send amount
}

// 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
type MsgRun struct {
thehowl marked this conversation as resolved.
Show resolved Hide resolved
Package *std.MemPackage // Package to run
Send string // Send amount
}

// Call executes a contract call on the blockchain.
Expand Down Expand Up @@ -106,59 +102,73 @@
return c.signAndBroadcastTxCommit(tx, cfg.AccountNumber, cfg.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.
func (c *Client) Run(cfg BaseTxCfg, msgs ...MsgRun) (*ctypes.ResultBroadcastTxCommit, error) {
thehowl marked this conversation as resolved.
Show resolved Hide resolved
// Validate required client fields
if err := c.validateSigner(); err != nil {
return nil, errors.Wrap(err, "validate signer")
return nil, err

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

View check run for this annotation

Codecov / codecov/patch

gno.land/pkg/gnoclient/client_txs.go#L108

Added line #L108 was not covered by tests
}
if err := c.validateRPCClient(); err != nil {
return nil, errors.Wrap(err, "validate RPC client")
return nil, err

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

View check run for this annotation

Codecov / codecov/patch

gno.land/pkg/gnoclient/client_txs.go#L111

Added line #L111 was 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)
// Validate base transaction config
if err := cfg.validateBaseTxConfig(); err != nil {
return nil, err

Check warning on line 116 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

Added line #L116 was 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")
// Parse MsgRun slice
vmMsgs := make([]vm.MsgRun, 0, len(msgs))
thehowl marked this conversation as resolved.
Show resolved Hide resolved
for _, msg := range msgs {
// Validate MsgCall fields
if err := msg.validateMsgRun(); err != nil {
return nil, err
}

// Parse send coins
send, err := std.ParseCoins(msg.Send)
if err != nil {
return nil, err
}

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#L128-L131

Added lines #L128 - L131 were not covered by tests

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

// Precompile and validate Gno syntax
if err = gno.PrecompileAndCheckMempkg(msg.Package); err != nil {
thehowl marked this conversation as resolved.
Show resolved Hide resolved
return nil, err
}

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

View check run for this annotation

Codecov / codecov/patch

gno.land/pkg/gnoclient/client_txs.go#L133-L138

Added lines #L133 - L138 were not covered by tests

msg.Package.Name = "main"
msg.Package.Path = "gno.land/r/" + caller.String() + "/run"

// Unwrap syntax sugar to vm.MsgCall slice
vmMsgs = append(vmMsgs, vm.MsgRun{
Caller: caller,
Package: msg.Package,
Send: send,
})

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

View check run for this annotation

Codecov / codecov/patch

gno.land/pkg/gnoclient/client_txs.go#L140-L148

Added lines #L140 - L148 were not covered by tests
}

caller := c.Signer.Info().GetAddress()
// Cast vm.MsgRun back into std.Msg
stdMsgs := make([]std.Msg, len(vmMsgs))
for i, msg := range vmMsgs {
stdMsgs[i] = msg
}

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

View check run for this annotation

Codecov / codecov/patch

gno.land/pkg/gnoclient/client_txs.go#L152-L155

Added lines #L152 - L155 were not covered by tests

// precompile and validate syntax
err = gno.PrecompileAndCheckMempkg(memPkg)
// Parse gas fee
gasFeeCoins, err := std.ParseCoin(cfg.GasFee)

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

View check run for this annotation

Codecov / codecov/patch

gno.land/pkg/gnoclient/client_txs.go#L158

Added line #L158 was not covered by tests
if err != nil {
return nil, errors.Wrap(err, "precompile and check")
return nil, err

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

View check run for this annotation

Codecov / codecov/patch

gno.land/pkg/gnoclient/client_txs.go#L160

Added line #L160 was 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,
}
// Pack transaction
tx := std.Tx{
Msgs: []std.Msg{msg},
Fee: std.NewFee(gasWanted, gasFeeCoins),
Msgs: stdMsgs,
Fee: std.NewFee(cfg.GasWanted, gasFeeCoins),

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

View check run for this annotation

Codecov / codecov/patch

gno.land/pkg/gnoclient/client_txs.go#L165-L166

Added lines #L165 - L166 were not covered by tests
Signatures: nil,
Memo: memo,
Memo: cfg.Memo,

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

View check run for this annotation

Codecov / codecov/patch

gno.land/pkg/gnoclient/client_txs.go#L168

Added line #L168 was not covered by tests
}

return c.signAndBroadcastTxCommit(tx, accountNumber, sequenceNumber)
return c.signAndBroadcastTxCommit(tx, cfg.AccountNumber, cfg.SequenceNumber)

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

View check run for this annotation

Codecov / codecov/patch

gno.land/pkg/gnoclient/client_txs.go#L171

Added line #L171 was not covered by tests
}

// signAndBroadcastTxCommit signs a transaction and broadcasts it, returning the result.
Expand Down
11 changes: 9 additions & 2 deletions gno.land/pkg/gnoclient/util.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package gnoclient

func (cfg BaseTxCfg) validateBaseTxConfig() error {
if cfg.GasWanted < 0 {
if cfg.GasWanted <= 0 {
return ErrInvalidGasWanted
}
if cfg.GasFee < "" {
thehowl marked this conversation as resolved.
Show resolved Hide resolved
if cfg.GasFee == "" {
return ErrInvalidGasFee
}

Expand All @@ -20,3 +20,10 @@
}
return nil
}

func (msg MsgRun) validateMsgRun() error {
if msg.Package == nil || len(msg.Package.Files) == 0 {
thehowl marked this conversation as resolved.
Show resolved Hide resolved
return ErrEmptyPackage
}
return nil

Check warning on line 28 in gno.land/pkg/gnoclient/util.go

View check run for this annotation

Codecov / codecov/patch

gno.land/pkg/gnoclient/util.go#L28

Added line #L28 was not covered by tests
}
Loading