Skip to content

Commit

Permalink
chore: In gnoclient, separate out SignTx and BroadcastTxCommit (#2641)
Browse files Browse the repository at this point in the history
We can use `gnokey maketx call`, `gnokey sign` and `gnokey broadcast` to
make a transaction and sign it separately before broadcast. We want to
support the same thing in the `gnoclient` API. (And we want to expose
this API in Gno Native Kit.)
* Split out new API function `NewCallTx` from `Call`
* Split out new API function `NewRunTx` from `Run`
* Split out new API function `NewSendTx` from `Send`
* Split out new API function `NewAddPackageTx` from `AddPackage`
* Split out new API functions `SignTx` and `BroadcastTxCommit` from
`signAndBroadcastTxCommit`
* In client_test.go and integration_test.go, add code to also test
signing separately

<details><summary>Contributors' checklist...</summary>

- [x] Added new tests, or not needed, or not feasible
- [x] Provided an example (e.g. screenshot) to aid review or the PR is
self-explanatory
- [x] Updated the official documentation or not needed
- [x] No breaking changes were made, or a `BREAKING CHANGE: xxx` message
was included in the description
</details>

---------

Signed-off-by: Jeff Thompson <jeff@thefirst.org>
  • Loading branch information
jefft0 authored Aug 26, 2024
1 parent b7dbed9 commit 651f5aa
Show file tree
Hide file tree
Showing 3 changed files with 230 additions and 19 deletions.
88 changes: 85 additions & 3 deletions gno.land/pkg/gnoclient/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,13 @@ func TestCallSingle(t *testing.T) {
res, err := client.Call(cfg, msg...)
assert.NoError(t, err)
require.NotNil(t, res)
assert.Equal(t, string(res.DeliverTx.Data), "it works!")
expected := "it works!"
assert.Equal(t, string(res.DeliverTx.Data), expected)

res, err = callSigningSeparately(t, client, cfg, msg...)
assert.NoError(t, err)
require.NotNil(t, res)
assert.Equal(t, string(res.DeliverTx.Data), expected)
}

func TestCallMultiple(t *testing.T) {
Expand Down Expand Up @@ -192,6 +198,10 @@ func TestCallMultiple(t *testing.T) {
res, err := client.Call(cfg, msg...)
assert.NoError(t, err)
assert.NotNil(t, res)

res, err = callSigningSeparately(t, client, cfg, msg...)
assert.NoError(t, err)
assert.NotNil(t, res)
}

func TestCallErrors(t *testing.T) {
Expand Down Expand Up @@ -656,7 +666,13 @@ func main() {
res, err := client.Run(cfg, msg)
assert.NoError(t, err)
require.NotNil(t, res)
assert.Equal(t, "hi gnoclient!\n", string(res.DeliverTx.Data))
expected := "hi gnoclient!\n"
assert.Equal(t, expected, string(res.DeliverTx.Data))

res, err = runSigningSeparately(t, client, cfg, msg)
assert.NoError(t, err)
require.NotNil(t, res)
assert.Equal(t, expected, string(res.DeliverTx.Data))
}

func TestRunMultiple(t *testing.T) {
Expand Down Expand Up @@ -740,7 +756,13 @@ func main() {
res, err := client.Run(cfg, msg1, msg2)
assert.NoError(t, err)
require.NotNil(t, res)
assert.Equal(t, "hi gnoclient!\nhi gnoclient!\n", string(res.DeliverTx.Data))
expected := "hi gnoclient!\nhi gnoclient!\n"
assert.Equal(t, expected, string(res.DeliverTx.Data))

res, err = runSigningSeparately(t, client, cfg, msg1, msg2)
assert.NoError(t, err)
require.NotNil(t, res)
assert.Equal(t, expected, string(res.DeliverTx.Data))
}

func TestRunErrors(t *testing.T) {
Expand Down Expand Up @@ -1326,3 +1348,63 @@ func TestLatestBlockHeightErrors(t *testing.T) {
})
}
}

// The same as client.Call, but test signing separately
func callSigningSeparately(t *testing.T, client Client, cfg BaseTxCfg, msgs ...vm.MsgCall) (*ctypes.ResultBroadcastTxCommit, error) {
t.Helper()
tx, err := NewCallTx(cfg, msgs...)
assert.NoError(t, err)
require.NotNil(t, tx)
signedTx, err := client.SignTx(*tx, cfg.AccountNumber, cfg.SequenceNumber)
assert.NoError(t, err)
require.NotNil(t, signedTx)
res, err := client.BroadcastTxCommit(signedTx)
assert.NoError(t, err)
require.NotNil(t, res)
return res, nil
}

// The same as client.Run, but test signing separately
func runSigningSeparately(t *testing.T, client Client, cfg BaseTxCfg, msgs ...vm.MsgRun) (*ctypes.ResultBroadcastTxCommit, error) {
t.Helper()
tx, err := NewRunTx(cfg, msgs...)
assert.NoError(t, err)
require.NotNil(t, tx)
signedTx, err := client.SignTx(*tx, cfg.AccountNumber, cfg.SequenceNumber)
assert.NoError(t, err)
require.NotNil(t, signedTx)
res, err := client.BroadcastTxCommit(signedTx)
assert.NoError(t, err)
require.NotNil(t, res)
return res, nil
}

// The same as client.Send, but test signing separately
func sendSigningSeparately(t *testing.T, client Client, cfg BaseTxCfg, msgs ...bank.MsgSend) (*ctypes.ResultBroadcastTxCommit, error) {
t.Helper()
tx, err := NewSendTx(cfg, msgs...)
assert.NoError(t, err)
require.NotNil(t, tx)
signedTx, err := client.SignTx(*tx, cfg.AccountNumber, cfg.SequenceNumber)
assert.NoError(t, err)
require.NotNil(t, signedTx)
res, err := client.BroadcastTxCommit(signedTx)
assert.NoError(t, err)
require.NotNil(t, res)
return res, nil
}

// The same as client.AddPackage, but test signing separately
func addPackageSigningSeparately(t *testing.T, client Client, cfg BaseTxCfg, msgs ...vm.MsgAddPackage) (*ctypes.ResultBroadcastTxCommit, error) {
t.Helper()
tx, err := NewAddPackageTx(cfg, msgs...)
assert.NoError(t, err)
require.NotNil(t, tx)
signedTx, err := client.SignTx(*tx, cfg.AccountNumber, cfg.SequenceNumber)
assert.NoError(t, err)
require.NotNil(t, signedTx)
res, err := client.BroadcastTxCommit(signedTx)
assert.NoError(t, err)
require.NotNil(t, res)
return res, nil
}
85 changes: 69 additions & 16 deletions gno.land/pkg/gnoclient/client_txs.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,16 @@ func (c *Client) Call(cfg BaseTxCfg, msgs ...vm.MsgCall) (*ctypes.ResultBroadcas
return nil, err
}

tx, err := NewCallTx(cfg, msgs...)
if err != nil {
return nil, err
}
return c.signAndBroadcastTxCommit(*tx, cfg.AccountNumber, cfg.SequenceNumber)
}

// NewCallTx makes an unsigned transaction from one or more MsgCall.
// The Caller field must be set.
func NewCallTx(cfg BaseTxCfg, msgs ...vm.MsgCall) (*std.Tx, error) {
// Validate base transaction config
if err := cfg.validateBaseTxConfig(); err != nil {
return nil, err
Expand All @@ -57,14 +67,12 @@ func (c *Client) Call(cfg BaseTxCfg, msgs ...vm.MsgCall) (*ctypes.ResultBroadcas
}

// Pack transaction
tx := std.Tx{
return &std.Tx{
Msgs: vmMsgs,
Fee: std.NewFee(cfg.GasWanted, gasFeeCoins),
Signatures: nil,
Memo: cfg.Memo,
}

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

// Run executes one or more MsgRun calls on the blockchain
Expand All @@ -77,6 +85,16 @@ func (c *Client) Run(cfg BaseTxCfg, msgs ...vm.MsgRun) (*ctypes.ResultBroadcastT
return nil, err
}

tx, err := NewRunTx(cfg, msgs...)
if err != nil {
return nil, err
}
return c.signAndBroadcastTxCommit(*tx, cfg.AccountNumber, cfg.SequenceNumber)
}

// NewRunTx makes an unsigned transaction from one or more MsgRun.
// The Caller field must be set.
func NewRunTx(cfg BaseTxCfg, msgs ...vm.MsgRun) (*std.Tx, error) {
// Validate base transaction config
if err := cfg.validateBaseTxConfig(); err != nil {
return nil, err
Expand All @@ -99,14 +117,12 @@ func (c *Client) Run(cfg BaseTxCfg, msgs ...vm.MsgRun) (*ctypes.ResultBroadcastT
}

// Pack transaction
tx := std.Tx{
return &std.Tx{
Msgs: vmMsgs,
Fee: std.NewFee(cfg.GasWanted, gasFeeCoins),
Signatures: nil,
Memo: cfg.Memo,
}

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

// Send executes one or more MsgSend calls on the blockchain
Expand All @@ -119,6 +135,16 @@ func (c *Client) Send(cfg BaseTxCfg, msgs ...bank.MsgSend) (*ctypes.ResultBroadc
return nil, err
}

tx, err := NewSendTx(cfg, msgs...)
if err != nil {
return nil, err
}
return c.signAndBroadcastTxCommit(*tx, cfg.AccountNumber, cfg.SequenceNumber)
}

// NewSendTx makes an unsigned transaction from one or more MsgSend.
// The FromAddress field must be set.
func NewSendTx(cfg BaseTxCfg, msgs ...bank.MsgSend) (*std.Tx, error) {
// Validate base transaction config
if err := cfg.validateBaseTxConfig(); err != nil {
return nil, err
Expand All @@ -141,14 +167,12 @@ func (c *Client) Send(cfg BaseTxCfg, msgs ...bank.MsgSend) (*ctypes.ResultBroadc
}

// Pack transaction
tx := std.Tx{
return &std.Tx{
Msgs: vmMsgs,
Fee: std.NewFee(cfg.GasWanted, gasFeeCoins),
Signatures: nil,
Memo: cfg.Memo,
}

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

// AddPackage executes one or more AddPackage calls on the blockchain
Expand All @@ -161,6 +185,16 @@ func (c *Client) AddPackage(cfg BaseTxCfg, msgs ...vm.MsgAddPackage) (*ctypes.Re
return nil, err
}

tx, err := NewAddPackageTx(cfg, msgs...)
if err != nil {
return nil, err
}
return c.signAndBroadcastTxCommit(*tx, cfg.AccountNumber, cfg.SequenceNumber)
}

// NewAddPackageTx makes an unsigned transaction from one or more MsgAddPackage.
// The Creator field must be set.
func NewAddPackageTx(cfg BaseTxCfg, msgs ...vm.MsgAddPackage) (*std.Tx, error) {
// Validate base transaction config
if err := cfg.validateBaseTxConfig(); err != nil {
return nil, err
Expand All @@ -183,18 +217,29 @@ func (c *Client) AddPackage(cfg BaseTxCfg, msgs ...vm.MsgAddPackage) (*ctypes.Re
}

// Pack transaction
tx := std.Tx{
return &std.Tx{
Msgs: vmMsgs,
Fee: std.NewFee(cfg.GasWanted, gasFeeCoins),
Signatures: nil,
Memo: cfg.Memo,
}

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

// signAndBroadcastTxCommit signs a transaction and broadcasts it, returning the result
func (c *Client) signAndBroadcastTxCommit(tx std.Tx, accountNumber, sequenceNumber uint64) (*ctypes.ResultBroadcastTxCommit, error) {
signedTx, err := c.SignTx(tx, accountNumber, sequenceNumber)
if err != nil {
return nil, err
}
return c.BroadcastTxCommit(signedTx)
}

// SignTx signs a transaction and returns a signed tx ready for broadcasting.
// If accountNumber or sequenceNumber is 0 then query the blockchain for the value.
func (c *Client) SignTx(tx std.Tx, accountNumber, sequenceNumber uint64) (*std.Tx, error) {
if err := c.validateSigner(); err != nil {
return nil, err
}
caller, err := c.Signer.Info()
if err != nil {
return nil, err
Expand All @@ -218,7 +263,15 @@ func (c *Client) signAndBroadcastTxCommit(tx std.Tx, accountNumber, sequenceNumb
if err != nil {
return nil, errors.Wrap(err, "sign")
}
return signedTx, nil
}

// BroadcastTxCommit marshals and broadcasts the signed transaction, returning the result.
// If the result has a delivery error, then return a wrapped error.
func (c *Client) BroadcastTxCommit(signedTx *std.Tx) (*ctypes.ResultBroadcastTxCommit, error) {
if err := c.validateRPCClient(); err != nil {
return nil, err
}
bz, err := amino.Marshal(signedTx)
if err != nil {
return nil, errors.Wrap(err, "marshaling tx binary bytes")
Expand Down
Loading

0 comments on commit 651f5aa

Please sign in to comment.