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>
This commit is contained in:
Amaury
2021-03-25 10:52:54 +00:00
committed by GitHub
co-authored by Marie Gauthier Alessio Treglia mergify[bot]
parent 7ac436d2f7
commit 1fa2c22d8a
14 changed files with 809 additions and 751 deletions
+23 -32
View File
@@ -2,11 +2,13 @@ package tx
import (
"bufio"
"context"
"errors"
"fmt"
"net/http"
"os"
gogogrpc "github.com/gogo/protobuf/grpc"
"github.com/spf13/pflag"
"github.com/cosmos/cosmos-sdk/client"
@@ -20,7 +22,6 @@ import (
"github.com/cosmos/cosmos-sdk/types/tx"
"github.com/cosmos/cosmos-sdk/types/tx/signing"
authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing"
authtx "github.com/cosmos/cosmos-sdk/x/auth/tx"
)
// GenerateOrBroadcastTxCLI will either generate and print and unsigned transaction
@@ -50,7 +51,7 @@ func GenerateTx(clientCtx client.Context, txf Factory, msgs ...sdk.Msg) error {
return errors.New("cannot estimate gas in offline mode")
}
_, adjusted, err := CalculateGas(clientCtx.QueryWithData, txf, msgs...)
_, adjusted, err := CalculateGas(clientCtx, txf, msgs...)
if err != nil {
return err
}
@@ -76,13 +77,13 @@ func GenerateTx(clientCtx client.Context, txf Factory, msgs ...sdk.Msg) error {
// given set of messages. It will also simulate gas requirements if necessary.
// It will return an error upon failure.
func BroadcastTx(clientCtx client.Context, txf Factory, msgs ...sdk.Msg) error {
txf, err := PrepareFactory(clientCtx, txf)
txf, err := prepareFactory(clientCtx, txf)
if err != nil {
return err
}
if txf.SimulateAndExecute() || clientCtx.Simulate {
_, adjusted, err := CalculateGas(clientCtx.QueryWithData, txf, msgs...)
_, adjusted, err := CalculateGas(clientCtx, txf, msgs...)
if err != nil {
return err
}
@@ -142,8 +143,9 @@ func BroadcastTx(clientCtx client.Context, txf Factory, msgs ...sdk.Msg) error {
// BaseReq. Upon any error, the error will be written to the http.ResponseWriter.
// Note that this function returns the legacy StdTx Amino JSON format for compatibility
// with legacy clients.
// Deprecated: We are removing Amino soon.
func WriteGeneratedTxResponse(
ctx client.Context, w http.ResponseWriter, br rest.BaseReq, msgs ...sdk.Msg,
clientCtx client.Context, w http.ResponseWriter, br rest.BaseReq, msgs ...sdk.Msg,
) {
gasAdj, ok := rest.ParseFloat64OrReturnBadRequest(w, br.GasAdjustment, flags.DefaultGasAdjustment)
if !ok {
@@ -163,7 +165,7 @@ func WriteGeneratedTxResponse(
WithMemo(br.Memo).
WithChainID(br.ChainID).
WithSimulateAndExecute(br.Simulate).
WithTxConfig(ctx.TxConfig).
WithTxConfig(clientCtx.TxConfig).
WithTimeoutHeight(br.TimeoutHeight)
if br.Simulate || gasSetting.Simulate {
@@ -172,7 +174,7 @@ func WriteGeneratedTxResponse(
return
}
_, adjusted, err := CalculateGas(ctx.QueryWithData, txf, msgs...)
_, adjusted, err := CalculateGas(clientCtx, txf, msgs...)
if rest.CheckInternalServerError(w, err) {
return
}
@@ -180,7 +182,7 @@ func WriteGeneratedTxResponse(
txf = txf.WithGas(adjusted)
if br.Simulate {
rest.WriteSimulationResponse(w, ctx.LegacyAmino, txf.Gas())
rest.WriteSimulationResponse(w, clientCtx.LegacyAmino, txf.Gas())
return
}
}
@@ -190,12 +192,12 @@ func WriteGeneratedTxResponse(
return
}
stdTx, err := ConvertTxToStdTx(ctx.LegacyAmino, tx.GetTx())
stdTx, err := ConvertTxToStdTx(clientCtx.LegacyAmino, tx.GetTx())
if rest.CheckInternalServerError(w, err) {
return
}
output, err := ctx.LegacyAmino.MarshalJSON(stdTx)
output, err := clientCtx.LegacyAmino.MarshalJSON(stdTx)
if rest.CheckInternalServerError(w, err) {
return
}
@@ -268,46 +270,35 @@ func BuildSimTx(txf Factory, msgs ...sdk.Msg) ([]byte, error) {
return nil, err
}
protoProvider, ok := txb.(authtx.ProtoTxProvider)
if !ok {
return nil, fmt.Errorf("cannot simulate amino tx")
}
simReq := tx.SimulateRequest{Tx: protoProvider.GetProtoTx()}
return simReq.Marshal()
return txf.txConfig.TxEncoder()(txb.GetTx())
}
// CalculateGas simulates the execution of a transaction and returns the
// simulation response obtained by the query and the adjusted gas amount.
func CalculateGas(
queryFunc func(string, []byte) ([]byte, int64, error), txf Factory, msgs ...sdk.Msg,
) (tx.SimulateResponse, uint64, error) {
clientCtx gogogrpc.ClientConn, txf Factory, msgs ...sdk.Msg,
) (*tx.SimulateResponse, uint64, error) {
txBytes, err := BuildSimTx(txf, msgs...)
if err != nil {
return tx.SimulateResponse{}, 0, err
return nil, 0, err
}
// TODO This should use the generated tx service Client.
// https://github.com/cosmos/cosmos-sdk/issues/7726
bz, _, err := queryFunc("/cosmos.tx.v1beta1.Service/Simulate", txBytes)
txSvcClient := tx.NewServiceClient(clientCtx)
simRes, err := txSvcClient.Simulate(context.Background(), &tx.SimulateRequest{
TxBytes: txBytes,
})
if err != nil {
return tx.SimulateResponse{}, 0, err
}
var simRes tx.SimulateResponse
if err := simRes.Unmarshal(bz); err != nil {
return tx.SimulateResponse{}, 0, err
return nil, 0, err
}
return simRes, uint64(txf.GasAdjustment() * float64(simRes.GasInfo.GasUsed)), nil
}
// PrepareFactory ensures the account defined by ctx.GetFromAddress() exists and
// prepareFactory ensures the account defined by ctx.GetFromAddress() exists and
// if the account number and/or the account sequence number are zero (not set),
// they will be queried for and set on the provided Factory. A new Factory with
// the updated fields will be returned.
func PrepareFactory(clientCtx client.Context, txf Factory) (Factory, error) {
func prepareFactory(clientCtx client.Context, txf Factory) (Factory, error) {
from := clientCtx.GetFromAddress()
if err := txf.accountRetriever.EnsureExists(clientCtx, from); err != nil {
+33 -24
View File
@@ -1,10 +1,12 @@
package tx_test
import (
"errors"
gocontext "context"
"fmt"
"testing"
"github.com/stretchr/testify/require"
"google.golang.org/grpc"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/tx"
@@ -24,30 +26,34 @@ func NewTestTxConfig() client.TxConfig {
return cfg.TxConfig
}
func TestCalculateGas(t *testing.T) {
makeQueryFunc := func(gasUsed uint64, wantErr bool) func(string, []byte) ([]byte, int64, error) {
return func(string, []byte) ([]byte, int64, error) {
if wantErr {
return nil, 0, errors.New("query failed")
}
simRes := &txtypes.SimulateResponse{
GasInfo: &sdk.GasInfo{GasUsed: gasUsed, GasWanted: gasUsed},
Result: &sdk.Result{Data: []byte("tx data"), Log: "log"},
}
// mockContext is a mock client.Context to return abitrary simulation response, used to
// unit test CalculateGas.
type mockContext struct {
gasUsed uint64
wantErr bool
}
bz, err := simRes.Marshal()
if err != nil {
return nil, 0, err
}
return bz, 0, nil
}
func (m mockContext) Invoke(grpcCtx gocontext.Context, method string, req, reply interface{}, opts ...grpc.CallOption) (err error) {
if m.wantErr {
return fmt.Errorf("mock err")
}
*(reply.(*txtypes.SimulateResponse)) = txtypes.SimulateResponse{
GasInfo: &sdk.GasInfo{GasUsed: m.gasUsed, GasWanted: m.gasUsed},
Result: &sdk.Result{Data: []byte("tx data"), Log: "log"},
}
return nil
}
func (mockContext) NewStream(gocontext.Context, *grpc.StreamDesc, string, ...grpc.CallOption) (grpc.ClientStream, error) {
panic("not implemented")
}
func TestCalculateGas(t *testing.T) {
type args struct {
queryFuncGasUsed uint64
queryFuncWantErr bool
adjustment float64
mockGasUsed uint64
mockWantErr bool
adjustment float64
}
testCases := []struct {
@@ -70,8 +76,11 @@ func TestCalculateGas(t *testing.T) {
WithTxConfig(txCfg).WithSignMode(txCfg.SignModeHandler().DefaultMode())
t.Run(stc.name, func(t *testing.T) {
queryFunc := makeQueryFunc(stc.args.queryFuncGasUsed, stc.args.queryFuncWantErr)
simRes, gotAdjusted, err := tx.CalculateGas(queryFunc, txf.WithGasAdjustment(stc.args.adjustment))
mockClientCtx := mockContext{
gasUsed: tc.args.mockGasUsed,
wantErr: tc.args.mockWantErr,
}
simRes, gotAdjusted, err := tx.CalculateGas(mockClientCtx, txf.WithGasAdjustment(stc.args.adjustment))
if stc.expPass {
require.NoError(t, err)
require.Equal(t, simRes.GasInfo.GasUsed, stc.wantEstimate)
@@ -79,7 +88,7 @@ func TestCalculateGas(t *testing.T) {
require.NotNil(t, simRes.Result)
} else {
require.Error(t, err)
require.Nil(t, simRes.Result)
require.Nil(t, simRes)
}
})
}