From eeea5ebf89d3101e38cb67ac4688407e2ef97fe0 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Thu, 19 Mar 2020 20:03:49 -0300 Subject: [PATCH 1/5] baseapp: add sdk.Result to simulation response --- CHANGELOG.md | 2 ++ baseapp/abci.go | 12 ++++++++++-- baseapp/baseapp_test.go | 9 ++++++--- types/result.go | 9 ++++++++- 4 files changed, 26 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b505678d5ad..4f008aaec1d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -70,6 +70,8 @@ to now accept a `codec.JSONMarshaler` for modular serialization of genesis state * (crypto/keys) [\#5735](https://github.com/cosmos/cosmos-sdk/pull/5735) Keyring's Update() function is now no-op. * (types/rest) [\#5779](https://github.com/cosmos/cosmos-sdk/pull/5779) Drop unused Parse{Int64OrReturnBadRequest,QueryParamBool}() functions. * (keys) [\#5820](https://github.com/cosmos/cosmos-sdk/pull/5820/) Removed method CloseDB from Keybase interface. +* (baseapp) [\#5837](https://github.com/cosmos/cosmos-sdk/issues/5837) Transaction simulation now returns a `SimulationResponse` which contains the `GasInfo` and +`Result` from the execution. ### Features diff --git a/baseapp/abci.go b/baseapp/abci.go index 834930196df5..e984fe0edf24 100644 --- a/baseapp/abci.go +++ b/baseapp/abci.go @@ -326,12 +326,20 @@ func handleQueryApp(app *BaseApp, path []string, req abci.RequestQuery) abci.Res return sdkerrors.QueryResult(sdkerrors.Wrap(err, "failed to decode tx")) } - gInfo, _, _ := app.Simulate(txBytes, tx) + gInfo, res, err := app.Simulate(txBytes, tx) + if err != nil { + return sdkerrors.QueryResult(sdkerrors.Wrap(err, "failed to simulate tx")) + } + + simRes := sdk.SimulationResponse{ + GasInfo: gInfo, + Result: res, + } return abci.ResponseQuery{ Codespace: sdkerrors.RootCodespace, Height: req.Height, - Value: codec.Cdc.MustMarshalBinaryBare(gInfo.GasUsed), + Value: codec.Cdc.MustMarshalBinaryBare(simRes), } case "version": diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index 2350ba64ceeb..2da5b95f6f00 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -935,10 +935,13 @@ func TestSimulateTx(t *testing.T) { queryResult := app.Query(query) require.True(t, queryResult.IsOK(), queryResult.Log) - var res uint64 - err = codec.Cdc.UnmarshalBinaryBare(queryResult.Value, &res) + var simRes sdk.SimulationResponse + err = codec.Cdc.UnmarshalBinaryBare(queryResult.Value, &simRes) require.NoError(t, err) - require.Equal(t, gasConsumed, res) + require.Equal(t, gInfo, simRes.GasInfo) + require.Equal(t, result.Log, simRes.Result.Log) + require.Equal(t, result.Events, simRes.Result.Events) + require.True(t, bytes.Equal(result.Data, simRes.Result.Data)) app.EndBlock(abci.RequestEndBlock{}) app.Commit() } diff --git a/types/result.go b/types/result.go index 9530be01d25c..54cc0e05330b 100644 --- a/types/result.go +++ b/types/result.go @@ -17,7 +17,7 @@ type GasInfo struct { // GasWanted is the maximum units of work we allow this tx to perform. GasWanted uint64 - // GasUsed is the amount of gas actually consumed. NOTE: unimplemented + // GasUsed is the amount of gas actually consumed. GasUsed uint64 } @@ -35,6 +35,13 @@ type Result struct { Events Events } +// SimulationResponse defines the response generated when a transaction is successfully +// simulated by the Baseapp. +type SimulationResponse struct { + GasInfo + Result *Result +} + // ABCIMessageLogs represents a slice of ABCIMessageLog. type ABCIMessageLogs []ABCIMessageLog From 3d2746434f04903c6b539769fbc25626fd72dad7 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Thu, 19 Mar 2020 22:40:07 -0300 Subject: [PATCH 2/5] authclient: update txBuilder to return sdk.SimulationResponse --- x/auth/client/tx.go | 33 ++++++++++++++------------- x/auth/client/tx_test.go | 48 ++++++++++++++++++++++++++-------------- 2 files changed, 47 insertions(+), 34 deletions(-) diff --git a/x/auth/client/tx.go b/x/auth/client/tx.go index 9036d7444d9f..3bd61a8e11fe 100644 --- a/x/auth/client/tx.go +++ b/x/auth/client/tx.go @@ -129,26 +129,26 @@ func EnrichWithGas(txBldr authtypes.TxBuilder, cliCtx context.CLIContext, msgs [ } // CalculateGas simulates the execution of a transaction and returns -// both the estimate obtained by the query and the adjusted amount. +// the simulation response obtained by the query and the adjusted amount. func CalculateGas( queryFunc func(string, []byte) ([]byte, int64, error), cdc *codec.Codec, txBytes []byte, adjustment float64, -) (estimate, adjusted uint64, err error) { +) (sdk.SimulationResponse, uint64, error) { // run a simulation (via /app/simulate query) to // estimate gas and update TxBuilder accordingly rawRes, _, err := queryFunc("/app/simulate", txBytes) if err != nil { - return estimate, adjusted, err + return sdk.SimulationResponse{}, 0, err } - estimate, err = parseQueryResponse(cdc, rawRes) + simRes, err := parseQueryResponse(cdc, rawRes) if err != nil { - return + return sdk.SimulationResponse{}, 0, err } - adjusted = adjustGasEstimate(estimate, adjustment) - return estimate, adjusted, nil + adjusted := adjustGasEstimate(simRes.GasUsed, adjustment) + return simRes, adjusted, nil } // PrintUnsignedStdTx builds an unsigned StdTx and prints it to os.Stdout. @@ -272,28 +272,27 @@ func GetTxEncoder(cdc *codec.Codec) (encoder sdk.TxEncoder) { } // nolint -// SimulateMsgs simulates the transaction and returns the gas estimate and the adjusted value. -func simulateMsgs(txBldr authtypes.TxBuilder, cliCtx context.CLIContext, msgs []sdk.Msg) (estimated, adjusted uint64, err error) { +// SimulateMsgs simulates the transaction and returns the simulation response and the adjusted value. +func simulateMsgs(txBldr authtypes.TxBuilder, cliCtx context.CLIContext, msgs []sdk.Msg) (sdk.SimulationResponse, uint64, error) { txBytes, err := txBldr.BuildTxForSim(msgs) if err != nil { - return + return sdk.SimulationResponse{}, 0, err } - estimated, adjusted, err = CalculateGas(cliCtx.QueryWithData, cliCtx.Codec, txBytes, txBldr.GasAdjustment()) - return + return CalculateGas(cliCtx.QueryWithData, cliCtx.Codec, txBytes, txBldr.GasAdjustment()) } func adjustGasEstimate(estimate uint64, adjustment float64) uint64 { return uint64(adjustment * float64(estimate)) } -func parseQueryResponse(cdc *codec.Codec, rawRes []byte) (uint64, error) { - var gasUsed uint64 - if err := cdc.UnmarshalBinaryBare(rawRes, &gasUsed); err != nil { - return 0, err +func parseQueryResponse(cdc *codec.Codec, rawRes []byte) (sdk.SimulationResponse, error) { + var simRes sdk.SimulationResponse + if err := cdc.UnmarshalBinaryBare(rawRes, &simRes); err != nil { + return sdk.SimulationResponse{}, err } - return gasUsed, nil + return simRes, nil } // PrepareTxBuilder populates a TxBuilder in preparation for the build of a Tx. diff --git a/x/auth/client/tx_test.go b/x/auth/client/tx_test.go index 643f253a21f8..682868555b5b 100644 --- a/x/auth/client/tx_test.go +++ b/x/auth/client/tx_test.go @@ -7,8 +7,8 @@ import ( "os" "testing" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/crypto/ed25519" "github.com/cosmos/cosmos-sdk/codec" @@ -23,13 +23,18 @@ var ( func TestParseQueryResponse(t *testing.T) { cdc := makeCodec() - sdkResBytes := cdc.MustMarshalBinaryBare(uint64(10)) - gas, err := parseQueryResponse(cdc, sdkResBytes) - assert.Equal(t, gas, uint64(10)) - assert.Nil(t, err) - gas, err = parseQueryResponse(cdc, []byte("fuzzy")) - assert.Equal(t, gas, uint64(0)) - assert.Error(t, err) + simRes := sdk.SimulationResponse{ + GasInfo: sdk.GasInfo{GasUsed: 10, GasWanted: 20}, + Result: nil, + } + + bz := cdc.MustMarshalBinaryBare(simRes) + res, err := parseQueryResponse(cdc, bz) + require.NoError(t, err) + require.Equal(t, 10, int(res.GasInfo.GasUsed)) + + res, err = parseQueryResponse(cdc, []byte("fuzzy")) + require.Error(t, err) } func TestCalculateGas(t *testing.T) { @@ -37,9 +42,14 @@ 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("") + return nil, 0, errors.New("query failed") + } + simRes := sdk.SimulationResponse{ + GasInfo: sdk.GasInfo{GasUsed: gasUsed, GasWanted: gasUsed}, + Result: nil, } - return cdc.MustMarshalBinaryBare(gasUsed), 0, nil + + return cdc.MustMarshalBinaryBare(simRes), 0, nil } } @@ -54,20 +64,24 @@ func TestCalculateGas(t *testing.T) { args args wantEstimate uint64 wantAdjusted uint64 - wantErr bool + expPass bool }{ - {"error", args{0, true, 1.2}, 0, 0, true}, - {"adjusted gas", args{10, false, 1.2}, 10, 12, false}, + {"error", args{0, true, 1.2}, 0, 0, false}, + {"adjusted gas", args{10, false, 1.2}, 10, 12, true}, } for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { queryFunc := makeQueryFunc(tt.args.queryFuncGasUsed, tt.args.queryFuncWantErr) - gotEstimate, gotAdjusted, err := CalculateGas(queryFunc, cdc, []byte(""), tt.args.adjustment) - assert.Equal(t, err != nil, tt.wantErr) - assert.Equal(t, gotEstimate, tt.wantEstimate) - assert.Equal(t, gotAdjusted, tt.wantAdjusted) + simRes, gotAdjusted, err := CalculateGas(queryFunc, cdc, []byte(""), tt.args.adjustment) + if tt.expPass { + require.NoError(t, err) + } else { + require.Error(t, err) + require.Equal(t, simRes.GasInfo.GasUsed, tt.wantEstimate) + require.Equal(t, gotAdjusted, tt.wantAdjusted) + } }) } } From 38f4b084c1c0187d4d957cb452b67a41a6b224f0 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Thu, 19 Mar 2020 22:42:34 -0300 Subject: [PATCH 3/5] authclient: update godoc for gas adjusted --- x/auth/client/tx.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/x/auth/client/tx.go b/x/auth/client/tx.go index 3bd61a8e11fe..09f3146e7cdf 100644 --- a/x/auth/client/tx.go +++ b/x/auth/client/tx.go @@ -129,7 +129,7 @@ func EnrichWithGas(txBldr authtypes.TxBuilder, cliCtx context.CLIContext, msgs [ } // CalculateGas simulates the execution of a transaction and returns -// the simulation response obtained by the query and the adjusted amount. +// the simulation response obtained by the query and the adjusted gas amount. func CalculateGas( queryFunc func(string, []byte) ([]byte, int64, error), cdc *codec.Codec, txBytes []byte, adjustment float64, @@ -271,8 +271,8 @@ func GetTxEncoder(cdc *codec.Codec) (encoder sdk.TxEncoder) { return encoder } -// nolint -// SimulateMsgs simulates the transaction and returns the simulation response and the adjusted value. +// simulateMsgs simulates the transaction and returns the simulation response and +// the adjusted gas value. func simulateMsgs(txBldr authtypes.TxBuilder, cliCtx context.CLIContext, msgs []sdk.Msg) (sdk.SimulationResponse, uint64, error) { txBytes, err := txBldr.BuildTxForSim(msgs) if err != nil { From 51d90451524403d0a6bb8fd2c3df775e7a6253ce Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Thu, 19 Mar 2020 22:46:35 -0300 Subject: [PATCH 4/5] authclient: fix tests --- x/auth/client/tx_test.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/x/auth/client/tx_test.go b/x/auth/client/tx_test.go index 682868555b5b..29d49a71738e 100644 --- a/x/auth/client/tx_test.go +++ b/x/auth/client/tx_test.go @@ -46,7 +46,7 @@ func TestCalculateGas(t *testing.T) { } simRes := sdk.SimulationResponse{ GasInfo: sdk.GasInfo{GasUsed: gasUsed, GasWanted: gasUsed}, - Result: nil, + Result: &sdk.Result{Data: []byte("tx data"), Log: "log"}, } return cdc.MustMarshalBinaryBare(simRes), 0, nil @@ -77,10 +77,12 @@ func TestCalculateGas(t *testing.T) { simRes, gotAdjusted, err := CalculateGas(queryFunc, cdc, []byte(""), tt.args.adjustment) if tt.expPass { require.NoError(t, err) - } else { - require.Error(t, err) require.Equal(t, simRes.GasInfo.GasUsed, tt.wantEstimate) require.Equal(t, gotAdjusted, tt.wantAdjusted) + require.NotNil(t, simRes.Result) + } else { + require.Error(t, err) + require.Nil(t, simRes.Result) } }) } From 2cf816d4575be1e2adcc6834dcd65b85e2903a59 Mon Sep 17 00:00:00 2001 From: Federico Kunze Date: Thu, 19 Mar 2020 22:48:20 -0300 Subject: [PATCH 5/5] minor test change --- x/auth/client/tx_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x/auth/client/tx_test.go b/x/auth/client/tx_test.go index 29d49a71738e..22e9586d76e6 100644 --- a/x/auth/client/tx_test.go +++ b/x/auth/client/tx_test.go @@ -25,13 +25,14 @@ func TestParseQueryResponse(t *testing.T) { cdc := makeCodec() simRes := sdk.SimulationResponse{ GasInfo: sdk.GasInfo{GasUsed: 10, GasWanted: 20}, - Result: nil, + Result: &sdk.Result{Data: []byte("tx data"), Log: "log"}, } bz := cdc.MustMarshalBinaryBare(simRes) res, err := parseQueryResponse(cdc, bz) require.NoError(t, err) require.Equal(t, 10, int(res.GasInfo.GasUsed)) + require.NotNil(t, res.Result) res, err = parseQueryResponse(cdc, []byte("fuzzy")) require.Error(t, err)