diff --git a/tests/systemtests/tx_test.go b/tests/systemtests/tx_test.go index 6d8074a340..e0cca5c724 100644 --- a/tests/systemtests/tx_test.go +++ b/tests/systemtests/tx_test.go @@ -8,6 +8,7 @@ import ( "encoding/json" "fmt" "os" + "strings" "testing" "github.com/stretchr/testify/require" @@ -1086,3 +1087,116 @@ func readTestAminoTxBinary(t *testing.T, aminoCodec *codec.LegacyAmino) ([]byte, require.NoError(t, err) return txJSONBytes, &stdTx } + +func TestSimulateTx_GasImprovements(t *testing.T) { + if !systest.IsV2() { + t.Skip("This test was made for testing gas improvements for v2. It doesn't work as is for v1, because of how the v1 handles out of gas issues compared to v2 (server error instead of proper response). This makes this test not compatible with v1.") + } + + systest.Sut.ResetChain(t) + + cli := systest.NewCLIWrapper(t, systest.Sut, systest.Verbose) + // get validator address + valAddr := cli.GetKeyAddr("node0") + // use node 1 as granter + granter := cli.GetKeyAddr("node1") + + require.NotEmpty(t, valAddr) + + // add new key + receiverAddr := cli.AddKey("account1") + + systest.Sut.StartChain(t) + + baseURL := systest.Sut.APIAddress() + + // node1 grant to node0 + grantCmdArgs := []string{"tx", "feegrant", "grant", granter, valAddr, "--chain-id=" + cli.ChainID()} + rsp := cli.Run(grantCmdArgs...) + txResult, found := cli.AwaitTxCommitted(rsp) + require.True(t, found) + systest.RequireTxSuccess(t, txResult) + + sendSimulateCmdArgs := []string{"tx", "bank", "send", valAddr, receiverAddr, fmt.Sprintf("%d%s", transferAmount, denom), "--chain-id=" + cli.ChainID(), "--sign-mode=direct", "--generate-only"} + bankSendCmdArgs := []string{"tx", "bank", "send", valAddr, receiverAddr, fmt.Sprintf("%d%s", transferAmount, denom), "--chain-id=" + cli.ChainID()} + testCases := []struct { + name string + simulateArgs []string + txArgs []string + }{ + { + "simulate without fees", + sendSimulateCmdArgs, + bankSendCmdArgs, + }, + { + "simulate with fees", + append(sendSimulateCmdArgs, "--fees=1stake"), + bankSendCmdArgs, + }, + { + "simulate without fee-granter", + sendSimulateCmdArgs, + append(bankSendCmdArgs, fmt.Sprintf("--fee-granter=%s", granter)), + }, + { + "simulate with fee-granter", + append(sendSimulateCmdArgs, fmt.Sprintf("--fee-granter=%s", granter)), + append(bankSendCmdArgs, fmt.Sprintf("--fee-granter=%s", granter)), + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + txlen := 1 + gasSimulated := make([]int64, txlen) + gasAdjustment := make([]float64, txlen) + + for i := 0; i < txlen; i++ { + // create unsign tx + res := cli.RunCommandWithArgs(tc.simulateArgs...) + txFile := systest.StoreTempFile(t, []byte(res)) + + res = cli.RunCommandWithArgs("tx", "sign", txFile.Name(), "--from="+valAddr, "--chain-id="+cli.ChainID(), "--keyring-backend=test", "--home="+systest.Sut.NodeDir(0)) + signedTxFile := systest.StoreTempFile(t, []byte(res)) + + res = cli.RunCommandWithArgs("tx", "encode", signedTxFile.Name()) + txBz, err := base64.StdEncoding.DecodeString(res) + require.NoError(t, err) + + reqBz, err := json.Marshal(&tx.SimulateRequest{TxBytes: txBz}) + require.NoError(t, err) + + resBz, err := testutil.PostRequest(fmt.Sprintf("%s/cosmos/tx/v1beta1/simulate", baseURL), "application/json", reqBz) + require.NoError(t, err) + gasUsed := gjson.Get(string(resBz), "gas_info.gas_used").Int() + require.True(t, gasUsed > 0) + gasSimulated[i] = gasUsed + } + + for i := 0; i < txlen; i++ { + // Submit tx with gas return from simulate + gasAdjustment[i] = 0.99 + // test valid transaction + shouldRerun := true + for shouldRerun { + gasAdjustment[i] = gasAdjustment[i] + 0.01 + gasUse := int64(gasAdjustment[i] * float64(gasSimulated[i])) + rsp := cli.Run(append(tc.txArgs, fmt.Sprintf("--gas=%d", gasUse))...) + // rerun if tx err and increase gas-adjustment by 1% + shouldRerun = strings.Contains(gjson.Get(rsp, "raw_log").String(), "out of gas") || gjson.Get(rsp, "gas_used").Int() == int64(0) || gjson.Get(rsp, "code").Int() != 0 + fmt.Println("gasAdjustment", i, gasAdjustment[i]) + } + } + + // Calculate average adjustments + total := 0.0 + for i := 0; i < txlen; i++ { + total = total + gasAdjustment[i] + } + average := total / float64(txlen) + result := fmt.Sprintf("Test case %s average gas adjustment is: %f", tc.name, average) + fmt.Println(result) + }) + } +}