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

baseapp: add sdk.Result to simulation response #5839

Merged
merged 5 commits into from
Mar 20, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
12 changes: 10 additions & 2 deletions baseapp/abci.go
Original file line number Diff line number Diff line change
Expand Up @@ -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":
Expand Down
9 changes: 6 additions & 3 deletions baseapp/baseapp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
Expand Down
9 changes: 8 additions & 1 deletion types/result.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand All @@ -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

Expand Down
35 changes: 17 additions & 18 deletions x/auth/client/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 gas 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.
Expand Down Expand Up @@ -271,29 +271,28 @@ func GetTxEncoder(cdc *codec.Codec) (encoder sdk.TxEncoder) {
return encoder
}

// 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 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 {
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.
Expand Down
51 changes: 34 additions & 17 deletions x/auth/client/tx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -23,23 +23,34 @@ 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: &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)
}

func TestCalculateGas(t *testing.T) {
cdc := makeCodec()
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: &sdk.Result{Data: []byte("tx data"), Log: "log"},
}
return cdc.MustMarshalBinaryBare(gasUsed), 0, nil

return cdc.MustMarshalBinaryBare(simRes), 0, nil
}
}

Expand All @@ -54,20 +65,26 @@ 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)
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)
}
})
}
}
Expand Down