Skip to content

Commit 1fa2c22

Browse files
amaury1093blushiAlessio Tregliamergify[bot]
authored
Prefer sending tx_bytes to Simulate gRPC endpoint (#8926)
* First run * Remove dead code * Make test pass * Proto gen * Fix lint * Add changelog * Fix tests * Fix test * Update x/auth/tx/service.go Co-authored-by: Marie Gauthier <marie.gauthier63@gmail.com> * Remove protoTxProvider * Add grpc-gateway test * Add comment * move to api breaking * lesser diff * remove conflict * empty commit to rerun CI * empty commit to rerun CI Co-authored-by: Marie Gauthier <marie.gauthier63@gmail.com> Co-authored-by: Alessio Treglia <alessio@tendermint.com> Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
1 parent 7ac436d commit 1fa2c22

File tree

14 files changed

+808
-750
lines changed

14 files changed

+808
-750
lines changed

CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ Ref: https://keepachangelog.com/en/1.0.0/
6565
* (x/upgrade) [\#8743](https://github.com/cosmos/cosmos-sdk/pull/8743) `UpgradeHandler` includes a new argument `VersionMap` which helps facilitate in-place migrations.
6666
* (x/auth) [\#8129](https://github.com/cosmos/cosmos-sdk/pull/8828) Updated `SigVerifiableTx.GetPubKeys` method signature to return error.
6767
* [\#8682](https://github.com/cosmos/cosmos-sdk/pull/8682) `ante.NewAnteHandler` updated to receive all positional params as `ante.HandlerOptions` struct. If required fields aren't set, throws error accordingly.
68+
* (client) [\#8926](https://github.com/cosmos/cosmos-sdk/pull/8926) `client/tx.PrepareFactory` has been converted to a private function, as it's only used internally.
69+
* (auth/tx) [\#8926](https://github.com/cosmos/cosmos-sdk/pull/8926) The `ProtoTxProvider` interface used as a workaround for transaction simulation has been removed.
6870

6971
### State Machine Breaking
7072

@@ -110,6 +112,10 @@ Ref: https://keepachangelog.com/en/1.0.0/
110112

111113
This release fixes security vulnerability identified in the simapp.
112114

115+
### Deprecated
116+
117+
* (grpc) [\#8926](https://github.com/cosmos/cosmos-sdk/pull/8926) The `tx` field in `SimulateRequest` has been deprecated, prefer to pass `tx_bytes` instead.
118+
113119
## [v0.42.0](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.42.0) - 2021-03-08
114120

115121
**IMPORTANT**: This release contains an important security fix for all non Cosmos Hub chains running Stargate version of the Cosmos SDK (>0.40). Non-hub chains should not be using any version of the SDK in the v0.40.x or v0.41.x release series. See [#8461](https://github.com/cosmos/cosmos-sdk/pull/8461) for more details.

client/tx/tx.go

+23-32
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@ package tx
22

33
import (
44
"bufio"
5+
"context"
56
"errors"
67
"fmt"
78
"net/http"
89
"os"
910

11+
gogogrpc "github.com/gogo/protobuf/grpc"
1012
"github.com/spf13/pflag"
1113

1214
"github.com/cosmos/cosmos-sdk/client"
@@ -20,7 +22,6 @@ import (
2022
"github.com/cosmos/cosmos-sdk/types/tx"
2123
"github.com/cosmos/cosmos-sdk/types/tx/signing"
2224
authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing"
23-
authtx "github.com/cosmos/cosmos-sdk/x/auth/tx"
2425
)
2526

2627
// GenerateOrBroadcastTxCLI will either generate and print and unsigned transaction
@@ -50,7 +51,7 @@ func GenerateTx(clientCtx client.Context, txf Factory, msgs ...sdk.Msg) error {
5051
return errors.New("cannot estimate gas in offline mode")
5152
}
5253

53-
_, adjusted, err := CalculateGas(clientCtx.QueryWithData, txf, msgs...)
54+
_, adjusted, err := CalculateGas(clientCtx, txf, msgs...)
5455
if err != nil {
5556
return err
5657
}
@@ -76,13 +77,13 @@ func GenerateTx(clientCtx client.Context, txf Factory, msgs ...sdk.Msg) error {
7677
// given set of messages. It will also simulate gas requirements if necessary.
7778
// It will return an error upon failure.
7879
func BroadcastTx(clientCtx client.Context, txf Factory, msgs ...sdk.Msg) error {
79-
txf, err := PrepareFactory(clientCtx, txf)
80+
txf, err := prepareFactory(clientCtx, txf)
8081
if err != nil {
8182
return err
8283
}
8384

8485
if txf.SimulateAndExecute() || clientCtx.Simulate {
85-
_, adjusted, err := CalculateGas(clientCtx.QueryWithData, txf, msgs...)
86+
_, adjusted, err := CalculateGas(clientCtx, txf, msgs...)
8687
if err != nil {
8788
return err
8889
}
@@ -142,8 +143,9 @@ func BroadcastTx(clientCtx client.Context, txf Factory, msgs ...sdk.Msg) error {
142143
// BaseReq. Upon any error, the error will be written to the http.ResponseWriter.
143144
// Note that this function returns the legacy StdTx Amino JSON format for compatibility
144145
// with legacy clients.
146+
// Deprecated: We are removing Amino soon.
145147
func WriteGeneratedTxResponse(
146-
ctx client.Context, w http.ResponseWriter, br rest.BaseReq, msgs ...sdk.Msg,
148+
clientCtx client.Context, w http.ResponseWriter, br rest.BaseReq, msgs ...sdk.Msg,
147149
) {
148150
gasAdj, ok := rest.ParseFloat64OrReturnBadRequest(w, br.GasAdjustment, flags.DefaultGasAdjustment)
149151
if !ok {
@@ -163,7 +165,7 @@ func WriteGeneratedTxResponse(
163165
WithMemo(br.Memo).
164166
WithChainID(br.ChainID).
165167
WithSimulateAndExecute(br.Simulate).
166-
WithTxConfig(ctx.TxConfig).
168+
WithTxConfig(clientCtx.TxConfig).
167169
WithTimeoutHeight(br.TimeoutHeight)
168170

169171
if br.Simulate || gasSetting.Simulate {
@@ -172,15 +174,15 @@ func WriteGeneratedTxResponse(
172174
return
173175
}
174176

175-
_, adjusted, err := CalculateGas(ctx.QueryWithData, txf, msgs...)
177+
_, adjusted, err := CalculateGas(clientCtx, txf, msgs...)
176178
if rest.CheckInternalServerError(w, err) {
177179
return
178180
}
179181

180182
txf = txf.WithGas(adjusted)
181183

182184
if br.Simulate {
183-
rest.WriteSimulationResponse(w, ctx.LegacyAmino, txf.Gas())
185+
rest.WriteSimulationResponse(w, clientCtx.LegacyAmino, txf.Gas())
184186
return
185187
}
186188
}
@@ -190,12 +192,12 @@ func WriteGeneratedTxResponse(
190192
return
191193
}
192194

193-
stdTx, err := ConvertTxToStdTx(ctx.LegacyAmino, tx.GetTx())
195+
stdTx, err := ConvertTxToStdTx(clientCtx.LegacyAmino, tx.GetTx())
194196
if rest.CheckInternalServerError(w, err) {
195197
return
196198
}
197199

198-
output, err := ctx.LegacyAmino.MarshalJSON(stdTx)
200+
output, err := clientCtx.LegacyAmino.MarshalJSON(stdTx)
199201
if rest.CheckInternalServerError(w, err) {
200202
return
201203
}
@@ -268,46 +270,35 @@ func BuildSimTx(txf Factory, msgs ...sdk.Msg) ([]byte, error) {
268270
return nil, err
269271
}
270272

271-
protoProvider, ok := txb.(authtx.ProtoTxProvider)
272-
if !ok {
273-
return nil, fmt.Errorf("cannot simulate amino tx")
274-
}
275-
simReq := tx.SimulateRequest{Tx: protoProvider.GetProtoTx()}
276-
277-
return simReq.Marshal()
273+
return txf.txConfig.TxEncoder()(txb.GetTx())
278274
}
279275

280276
// CalculateGas simulates the execution of a transaction and returns the
281277
// simulation response obtained by the query and the adjusted gas amount.
282278
func CalculateGas(
283-
queryFunc func(string, []byte) ([]byte, int64, error), txf Factory, msgs ...sdk.Msg,
284-
) (tx.SimulateResponse, uint64, error) {
279+
clientCtx gogogrpc.ClientConn, txf Factory, msgs ...sdk.Msg,
280+
) (*tx.SimulateResponse, uint64, error) {
285281
txBytes, err := BuildSimTx(txf, msgs...)
286282
if err != nil {
287-
return tx.SimulateResponse{}, 0, err
283+
return nil, 0, err
288284
}
289285

290-
// TODO This should use the generated tx service Client.
291-
// https://github.com/cosmos/cosmos-sdk/issues/7726
292-
bz, _, err := queryFunc("/cosmos.tx.v1beta1.Service/Simulate", txBytes)
286+
txSvcClient := tx.NewServiceClient(clientCtx)
287+
simRes, err := txSvcClient.Simulate(context.Background(), &tx.SimulateRequest{
288+
TxBytes: txBytes,
289+
})
293290
if err != nil {
294-
return tx.SimulateResponse{}, 0, err
295-
}
296-
297-
var simRes tx.SimulateResponse
298-
299-
if err := simRes.Unmarshal(bz); err != nil {
300-
return tx.SimulateResponse{}, 0, err
291+
return nil, 0, err
301292
}
302293

303294
return simRes, uint64(txf.GasAdjustment() * float64(simRes.GasInfo.GasUsed)), nil
304295
}
305296

306-
// PrepareFactory ensures the account defined by ctx.GetFromAddress() exists and
297+
// prepareFactory ensures the account defined by ctx.GetFromAddress() exists and
307298
// if the account number and/or the account sequence number are zero (not set),
308299
// they will be queried for and set on the provided Factory. A new Factory with
309300
// the updated fields will be returned.
310-
func PrepareFactory(clientCtx client.Context, txf Factory) (Factory, error) {
301+
func prepareFactory(clientCtx client.Context, txf Factory) (Factory, error) {
311302
from := clientCtx.GetFromAddress()
312303

313304
if err := txf.accountRetriever.EnsureExists(clientCtx, from); err != nil {

client/tx/tx_test.go

+32-23
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
package tx_test
22

33
import (
4-
"errors"
4+
gocontext "context"
5+
"fmt"
56
"testing"
67

78
"github.com/stretchr/testify/require"
9+
"google.golang.org/grpc"
810

911
"github.com/cosmos/cosmos-sdk/client"
1012
"github.com/cosmos/cosmos-sdk/client/tx"
@@ -24,30 +26,34 @@ func NewTestTxConfig() client.TxConfig {
2426
return cfg.TxConfig
2527
}
2628

27-
func TestCalculateGas(t *testing.T) {
28-
makeQueryFunc := func(gasUsed uint64, wantErr bool) func(string, []byte) ([]byte, int64, error) {
29-
return func(string, []byte) ([]byte, int64, error) {
30-
if wantErr {
31-
return nil, 0, errors.New("query failed")
32-
}
33-
simRes := &txtypes.SimulateResponse{
34-
GasInfo: &sdk.GasInfo{GasUsed: gasUsed, GasWanted: gasUsed},
35-
Result: &sdk.Result{Data: []byte("tx data"), Log: "log"},
36-
}
29+
// mockContext is a mock client.Context to return abitrary simulation response, used to
30+
// unit test CalculateGas.
31+
type mockContext struct {
32+
gasUsed uint64
33+
wantErr bool
34+
}
3735

38-
bz, err := simRes.Marshal()
39-
if err != nil {
40-
return nil, 0, err
41-
}
36+
func (m mockContext) Invoke(grpcCtx gocontext.Context, method string, req, reply interface{}, opts ...grpc.CallOption) (err error) {
37+
if m.wantErr {
38+
return fmt.Errorf("mock err")
39+
}
4240

43-
return bz, 0, nil
44-
}
41+
*(reply.(*txtypes.SimulateResponse)) = txtypes.SimulateResponse{
42+
GasInfo: &sdk.GasInfo{GasUsed: m.gasUsed, GasWanted: m.gasUsed},
43+
Result: &sdk.Result{Data: []byte("tx data"), Log: "log"},
4544
}
4645

46+
return nil
47+
}
48+
func (mockContext) NewStream(gocontext.Context, *grpc.StreamDesc, string, ...grpc.CallOption) (grpc.ClientStream, error) {
49+
panic("not implemented")
50+
}
51+
52+
func TestCalculateGas(t *testing.T) {
4753
type args struct {
48-
queryFuncGasUsed uint64
49-
queryFuncWantErr bool
50-
adjustment float64
54+
mockGasUsed uint64
55+
mockWantErr bool
56+
adjustment float64
5157
}
5258

5359
testCases := []struct {
@@ -70,16 +76,19 @@ func TestCalculateGas(t *testing.T) {
7076
WithTxConfig(txCfg).WithSignMode(txCfg.SignModeHandler().DefaultMode())
7177

7278
t.Run(stc.name, func(t *testing.T) {
73-
queryFunc := makeQueryFunc(stc.args.queryFuncGasUsed, stc.args.queryFuncWantErr)
74-
simRes, gotAdjusted, err := tx.CalculateGas(queryFunc, txf.WithGasAdjustment(stc.args.adjustment))
79+
mockClientCtx := mockContext{
80+
gasUsed: tc.args.mockGasUsed,
81+
wantErr: tc.args.mockWantErr,
82+
}
83+
simRes, gotAdjusted, err := tx.CalculateGas(mockClientCtx, txf.WithGasAdjustment(stc.args.adjustment))
7584
if stc.expPass {
7685
require.NoError(t, err)
7786
require.Equal(t, simRes.GasInfo.GasUsed, stc.wantEstimate)
7887
require.Equal(t, gotAdjusted, stc.wantAdjusted)
7988
require.NotNil(t, simRes.Result)
8089
} else {
8190
require.Error(t, err)
82-
require.Nil(t, simRes.Result)
91+
require.Nil(t, simRes)
8392
}
8493
})
8594
}

docs/core/proto-docs.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -7257,7 +7257,8 @@ RPC method.
72577257

72587258
| Field | Type | Label | Description |
72597259
| ----- | ---- | ----- | ----------- |
7260-
| `tx` | [Tx](#cosmos.tx.v1beta1.Tx) | | tx is the transaction to simulate. |
7260+
| `tx` | [Tx](#cosmos.tx.v1beta1.Tx) | | **Deprecated.** tx is the transaction to simulate. Deprecated. Send raw tx bytes instead. |
7261+
| `tx_bytes` | [bytes](#bytes) | | tx_bytes is the raw transaction. |
72617262

72627263

72637264

docs/run-node/txs.md

+4-16
Original file line numberDiff line numberDiff line change
@@ -305,15 +305,13 @@ func simulateTx() error {
305305
// Simulate the tx via gRPC. We create a new client for the Protobuf Tx
306306
// service.
307307
txClient := tx.NewServiceClient(grpcConn)
308-
// We then call the BroadcastTx method on this client.
309-
protoTx := txBuilderToProtoTx(txBuilder)
310-
if err != nil {
311-
return err
312-
}
308+
txBytes := /* Fill in with your signed transaction bytes. */
309+
310+
// We then call the Simulate method on this client.
313311
grpcRes, err := txClient.Simulate(
314312
context.Background(),
315313
&tx.SimulateRequest{
316-
Tx: protoTx,
314+
TxBytes: txBytes,
317315
},
318316
)
319317
if err != nil {
@@ -324,16 +322,6 @@ func simulateTx() error {
324322

325323
return nil
326324
}
327-
328-
// txBuilderToProtoTx converts a txBuilder into a proto tx.Tx.
329-
func txBuilderToProtoTx(txBuilder client.TxBuilder) (*tx.Tx, error) { // nolint
330-
protoProvider, ok := txBuilder.(authtx.ProtoTxProvider)
331-
if !ok {
332-
return nil, fmt.Errorf("expected proto tx builder, got %T", txBuilder)
333-
}
334-
335-
return protoProvider.GetProtoTx(), nil
336-
}
337325
```
338326

339327
## Using REST

proto/cosmos/tx/v1beta1/service.proto

+4-1
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,10 @@ message BroadcastTxResponse {
101101
// RPC method.
102102
message SimulateRequest {
103103
// tx is the transaction to simulate.
104-
cosmos.tx.v1beta1.Tx tx = 1;
104+
// Deprecated. Send raw tx bytes instead.
105+
cosmos.tx.v1beta1.Tx tx = 1 [deprecated=true];
106+
// tx_bytes is the raw transaction.
107+
bytes tx_bytes = 2;
105108
}
106109

107110
// SimulateResponse is the response type for the

simapp/simd/cmd/root.go

-3
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ import (
2525
"github.com/cosmos/cosmos-sdk/snapshots"
2626
"github.com/cosmos/cosmos-sdk/store"
2727
sdk "github.com/cosmos/cosmos-sdk/types"
28-
authclient "github.com/cosmos/cosmos-sdk/x/auth/client"
2928
authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli"
3029
"github.com/cosmos/cosmos-sdk/x/auth/types"
3130
vestingcli "github.com/cosmos/cosmos-sdk/x/auth/vesting/client/cli"
@@ -66,8 +65,6 @@ func NewRootCmd() (*cobra.Command, params.EncodingConfig) {
6665
}
6766

6867
func initRootCmd(rootCmd *cobra.Command, encodingConfig params.EncodingConfig) {
69-
authclient.Codec = encodingConfig.Marshaler
70-
7168
rootCmd.AddCommand(
7269
genutilcli.InitCmd(simapp.ModuleBasics, simapp.DefaultNodeHome),
7370
genutilcli.CollectGenTxsCmd(banktypes.GenesisBalancesIterator{}, simapp.DefaultNodeHome),

0 commit comments

Comments
 (0)