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(gnoclient): add MultiCall #1565

Merged
merged 45 commits into from
Feb 1, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
bf6a79f
wip save
leohhhn Jan 20, 2024
738f70c
add msgcall wrapper & add batchcall func
leohhhn Jan 21, 2024
2de3712
remove comment
leohhhn Jan 21, 2024
de83a5b
add comment
leohhhn Jan 21, 2024
05aedff
fix lint
leohhhn Jan 21, 2024
ebaf516
remove multicall func, update struct, remove aliasing
leohhhn Jan 22, 2024
9a5fd71
extract errors to pkg level
leohhhn Jan 22, 2024
bdb9f60
remove aliasing
leohhhn Jan 22, 2024
fb2416d
add preliminary tests, remove some error wrapping
leohhhn Jan 22, 2024
948dc9f
Merge branch 'master' into feat/gnoclient-batchcall
leohhhn Jan 23, 2024
79cbe36
Merge branch 'master' into feat/gnoclient-batchcall
leohhhn Jan 24, 2024
ced737b
inline caller
leohhhn Jan 25, 2024
9c91240
add baseTxCfg, modify Call flow
leohhhn Jan 25, 2024
c9e85ea
add basecfg validation, comments, errors
leohhhn Jan 25, 2024
bb828df
add more unit tests, comments
leohhhn Jan 25, 2024
175bd5e
made unit tests better
leohhhn Jan 25, 2024
f0958d8
add testcase
leohhhn Jan 25, 2024
3ad650e
Merge branch 'master' into feat/gnoclient-batchcall
leohhhn Jan 26, 2024
a6fb52d
make errors public
leohhhn Jan 29, 2024
8be378e
remove fmt.errorf
leohhhn Jan 29, 2024
def2e0b
make error names clearer
leohhhn Jan 29, 2024
4f9febf
mocking wip
leohhhn Jan 29, 2024
cb7496f
add rpcclient mock, wip tests
leohhhn Jan 31, 2024
2cd4af3
expand txbroadcast res
leohhhn Jan 31, 2024
9f6cd76
wip more tests
leohhhn Jan 31, 2024
23fb5b4
add full rpcclient mock infra
leohhhn Jan 31, 2024
3ecf8f0
simplify tests
leohhhn Jan 31, 2024
a190219
typo fix
leohhhn Jan 31, 2024
7caf692
remove comment
leohhhn Jan 31, 2024
085ce3f
fix lint
leohhhn Jan 31, 2024
849ede2
fix test err
leohhhn Jan 31, 2024
788ccb9
Merge branch 'master' into feat/gnoclient-batchcall
leohhhn Jan 31, 2024
800d188
parallel test
leohhhn Feb 1, 2024
9e8c7e2
replace assert with require
leohhhn Feb 1, 2024
a4bf5cb
add errors.Is
leohhhn Feb 1, 2024
44a0657
remove panics
leohhhn Feb 1, 2024
1bad3de
add pointer receivers
leohhhn Feb 1, 2024
a663e8e
make fmt
leohhhn Feb 1, 2024
cd707ad
Merge branch 'master' into feat/gnoclient-batchcall
leohhhn Feb 1, 2024
f6e78d2
update imports, fix assert.ErrorIs
leohhhn Feb 1, 2024
c7481f0
Merge branch 'master' into feat/gnoclient-batchcall
leohhhn Feb 1, 2024
bd9ac4b
make fmt
leohhhn Feb 1, 2024
0e6300e
remove double assert
leohhhn Feb 1, 2024
60b5963
rename mock to mock_test
leohhhn Feb 1, 2024
7354457
Merge branch 'master' into feat/gnoclient-batchcall
leohhhn Feb 1, 2024
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
5 changes: 2 additions & 3 deletions gno.land/pkg/gnoclient/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package gnoclient

import (
rpcclient "github.com/gnolang/gno/tm2/pkg/bft/rpc/client"
"github.com/gnolang/gno/tm2/pkg/errors"
)

// Client provides an interface for interacting with the blockchain.
Expand All @@ -14,15 +13,15 @@ type Client struct {
// validateSigner checks that the signer is correctly configured.
func (c Client) validateSigner() error {
if c.Signer == nil {
return errors.New("missing Signer")
return errMissingSigner
thehowl marked this conversation as resolved.
Show resolved Hide resolved
}
return nil
}

// validateRPCClient checks that the RPCClient is correctly configured.
func (c Client) validateRPCClient() error {
if c.RPCClient == nil {
return errors.New("missing RPCClient")
return errMissingRPCClient
}
return nil
}
104 changes: 104 additions & 0 deletions gno.land/pkg/gnoclient/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,107 @@

// XXX: need more test
}

func TestClient_Call(t *testing.T) {
t.Parallel()

config, _ := integration.TestingNodeConfig(t, gnoenv.RootDir())
node, remoteAddr := integration.TestingInMemoryNode(t, log.NewNopLogger(), config)
defer node.Stop()

signer := newInMemorySigner(t, config.TMConfig.ChainID())
rpcClient := rpcclient.NewHTTP(remoteAddr, "/websocket")
zivkovicmilos marked this conversation as resolved.
Show resolved Hide resolved

client := Client{
Signer: signer,
RPCClient: rpcClient,
}

cfg := CallCfg{
Msgs: []MsgCall{
{
PkgPath: "gno.land/r/demo/deep/very/deep",
FuncName: "Render",
Args: []string{""},
Send: "",
},
},
GasFee: "1000000ugnot",
GasWanted: 8000000,
AccountNumber: 0,
SequenceNumber: 0,
}

_, err := client.Call(cfg)
require.NoError(t, err)
zivkovicmilos marked this conversation as resolved.
Show resolved Hide resolved
}

func TestClient_Call_Errors(t *testing.T) {

Check failure on line 88 in gno.land/pkg/gnoclient/client_test.go

View workflow job for this annotation

GitHub Actions / lint

TestClient_Call_Errors's subtests should call t.Parallel (tparallel)
t.Parallel()

config, _ := integration.TestingNodeConfig(t, gnoenv.RootDir())
node, remoteAddr := integration.TestingInMemoryNode(t, log.NewNopLogger(), config)
defer node.Stop()

signer := newInMemorySigner(t, config.TMConfig.ChainID())
rpcClient := rpcclient.NewHTTP(remoteAddr, "/websocket")

testCases := []struct {
name string
client Client
cfg CallCfg
expectedError error
}{
{
name: "Invalid Signer",
client: Client{
nil,
rpcClient,
},
cfg: CallCfg{},
expectedError: errMissingSigner,
},
{
name: "Invalid RPCClient",
client: Client{
signer,
nil,
},
cfg: CallCfg{},
expectedError: errMissingRPCClient,
},
{
name: "Invalid PkgPath",
client: Client{
signer,
rpcClient,
},
cfg: CallCfg{
Msgs: []MsgCall{
{PkgPath: ""},
},
},
expectedError: errInvalidPkgPath,
},
{
name: "Invalid FuncName",
client: Client{
signer,
rpcClient,
},
cfg: CallCfg{
Msgs: []MsgCall{
{PkgPath: "random/path", FuncName: ""},
},
},
expectedError: errInvalidFuncName,
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
ajnavarro marked this conversation as resolved.
Show resolved Hide resolved
_, err := tc.client.Call(tc.cfg)
zivkovicmilos marked this conversation as resolved.
Show resolved Hide resolved
require.Equal(t, err, tc.expectedError)
zivkovicmilos marked this conversation as resolved.
Show resolved Hide resolved
})
}
}
101 changes: 55 additions & 46 deletions gno.land/pkg/gnoclient/client_txs.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,77 +8,86 @@ import (
"github.com/gnolang/gno/tm2/pkg/std"
)

// MsgCall - syntax sugar for vm.MsgCall
type MsgCall struct {
PkgPath string // Package path
FuncName string // Function name
Args []string // Function arguments
Send string // Send amount
}

// CallCfg contains configuration options for executing a contract call.
type CallCfg struct {
PkgPath string // Package path
FuncName string // Function name
Args []string // Function arguments
GasFee string // Gas fee
GasWanted int64 // Gas wanted
Send string // Send amount
AccountNumber uint64 // Account number
SequenceNumber uint64 // Sequence number
Memo string // Memo
Msgs []MsgCall
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.
if err := c.validateSigner(); err != nil {
return nil, errors.Wrap(err, "validate signer")
return nil, err
}
if err := c.validateRPCClient(); err != nil {
return nil, errors.Wrap(err, "validate RPC client")
return nil, err
}

pkgPath := cfg.PkgPath
funcName := cfg.FuncName
args := cfg.Args
gasWanted := cfg.GasWanted
gasFee := cfg.GasFee
send := cfg.Send
sequenceNumber := cfg.SequenceNumber
accountNumber := cfg.AccountNumber
memo := cfg.Memo

// Validate config.
if pkgPath == "" {
return nil, errors.New("missing PkgPath")
}
if funcName == "" {
return nil, errors.New("missing FuncName")
msgs := make([]vm.MsgCall, 0, len(cfg.Msgs))
for _, msg := range cfg.Msgs {
// Validate config.
if msg.PkgPath == "" {
return nil, errInvalidPkgPath
}
if msg.FuncName == "" {
return nil, errInvalidFuncName
}

// Parse send amount.
sendCoins, err := std.ParseCoins(msg.Send)
if err != nil {
return nil, errors.Wrap(err, "parsing send coins")
}

if err != nil {
return nil, errors.Wrap(err, "parsing gas fee coin")
}
zivkovicmilos marked this conversation as resolved.
Show resolved Hide resolved
zivkovicmilos marked this conversation as resolved.
Show resolved Hide resolved
zivkovicmilos marked this conversation as resolved.
Show resolved Hide resolved

caller := c.Signer.Info().GetAddress()
zivkovicmilos marked this conversation as resolved.
Show resolved Hide resolved

msgs = append(msgs, vm.MsgCall{
Caller: caller,
Send: sendCoins,
PkgPath: msg.PkgPath,
Func: msg.FuncName,
Args: msg.Args,
})
}

// Parse send amount.
sendCoins, err := std.ParseCoins(send)
if err != nil {
return nil, errors.Wrap(err, "parsing send coins")
// Cast vm.MsgCall back into std.Msg
stdMsgs := make([]std.Msg, len(msgs))
for i, msg := range msgs {
stdMsgs[i] = msg
}

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

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

// Construct message & transaction and marshal.
msg := vm.MsgCall{
Caller: caller,
Send: sendCoins,
PkgPath: pkgPath,
Func: funcName,
Args: args,
}
// Pack transaction
tx := std.Tx{
Msgs: []std.Msg{msg},
Fee: std.NewFee(gasWanted, gasFeeCoins),
Msgs: stdMsgs,
Fee: std.NewFee(cfg.GasWanted, gasFeeCoins),
Signatures: nil,
Memo: memo,
Memo: "",
zivkovicmilos marked this conversation as resolved.
Show resolved Hide resolved
}

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

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

import "errors"

var (
errInvalidPkgPath = errors.New("invalid pkgpath")
errInvalidFuncName = errors.New("invalid function name")
errMissingSigner = errors.New("missing Signer")
errMissingRPCClient = errors.New("missing RPCClient")
)
zivkovicmilos marked this conversation as resolved.
Show resolved Hide resolved
Loading