perf: benchmark end-to-end transaction submit (#16717)

This commit is contained in:
Elias Naur 2023-07-10 14:38:32 +02:00 committed by GitHub
parent febeced2e1
commit 217bb1001e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -0,0 +1,191 @@
package tx_test
import (
"context"
"fmt"
"testing"
"gotest.tools/v3/assert"
sdkmath "cosmossdk.io/math"
"cosmossdk.io/simapp"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
clienttx "github.com/cosmos/cosmos-sdk/client/tx"
addresscodec "github.com/cosmos/cosmos-sdk/codec/address"
"github.com/cosmos/cosmos-sdk/testutil/cli"
"github.com/cosmos/cosmos-sdk/testutil/network"
"github.com/cosmos/cosmos-sdk/testutil/testdata"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/tx"
"github.com/cosmos/cosmos-sdk/types/tx/signing"
authclient "github.com/cosmos/cosmos-sdk/x/auth/client"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
)
type E2EBenchmarkSuite struct {
cfg network.Config
network *network.Network
txHeight int64
queryClient tx.ServiceClient
txRes sdk.TxResponse
}
// BenchmarkTx is lifted from E2ETestSuite from this package, with irrelevant state checks removed.
//
// Benchmark results:
//
// On a M1 macOS, on commit ab77fe20d3c00ef4cb73dfd0c803c4593a3c8233:
//
// BenchmarkTx-8 4108 268935 ns/op
//
// By comparison, the benchmark modified for v0.47.0:
//
// BenchmarkTx-8 3772 301750 ns/op
func BenchmarkTx(b *testing.B) {
s := NewE2EBenchmarkSuite(b)
b.Cleanup(s.Close)
val := s.network.Validators[0]
txBuilder := mkTxBuilder(b, s)
// Convert the txBuilder to a tx.Tx.
protoTx, err := txBuilderToProtoTx(txBuilder)
assert.NilError(b, err)
// Encode the txBuilder to txBytes.
txBytes, err := val.ClientCtx.TxConfig.TxEncoder()(txBuilder.GetTx())
assert.NilError(b, err)
testCases := []struct {
name string
req *tx.SimulateRequest
}{
{"valid request with proto tx (deprecated)", &tx.SimulateRequest{Tx: protoTx}},
{"valid request with tx_bytes", &tx.SimulateRequest{TxBytes: txBytes}},
}
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for _, tc := range testCases {
// Broadcast the tx via gRPC via the validator's clientCtx (which goes
// through Tendermint).
res, err := s.queryClient.Simulate(context.Background(), tc.req)
assert.NilError(b, err)
// Check the result and gas used are correct.
//
// The 12 events are:
// - Sending Fee to the pool: coin_spent, coin_received, transfer and message.sender=<val1>
// - tx.* events: tx.fee, tx.acc_seq, tx.signature
// - Sending Amount to recipient: coin_spent, coin_received, transfer and message.sender=<val1>
// - Msg events: message.module=bank and message.action=/cosmos.bank.v1beta1.MsgSend (in one message)
assert.Equal(b, 12, len(res.GetResult().GetEvents()))
assert.Assert(b, res.GetGasInfo().GetGasUsed() > 0) // Gas used sometimes change, just check it's not empty.
}
}
}
func NewE2EBenchmarkSuite(tb testing.TB) *E2EBenchmarkSuite {
tb.Helper()
s := new(E2EBenchmarkSuite)
cfg := network.DefaultConfig(simapp.NewTestNetworkFixture)
cfg.NumValidators = 1
s.cfg = cfg
var err error
s.network, err = network.New(tb, tb.TempDir(), s.cfg)
assert.NilError(tb, err)
val := s.network.Validators[0]
assert.NilError(tb, s.network.WaitForNextBlock())
s.queryClient = tx.NewServiceClient(val.ClientCtx)
// Create a new MsgSend tx from val to itself.
out, err := cli.MsgSendExec(
val.ClientCtx,
val.Address,
val.Address,
sdk.NewCoins(
sdk.NewCoin(s.cfg.BondDenom, sdkmath.NewInt(10)),
),
addresscodec.NewBech32Codec("cosmos"),
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync),
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdkmath.NewInt(10))).String()),
fmt.Sprintf("--gas=%d", flags.DefaultGasLimit),
fmt.Sprintf("--%s=foobar", flags.FlagNote),
)
assert.NilError(tb, err)
assert.NilError(tb, val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &s.txRes))
assert.Equal(tb, uint32(0), s.txRes.Code, s.txRes)
out, err = cli.MsgSendExec(
val.ClientCtx,
val.Address,
val.Address,
sdk.NewCoins(
sdk.NewCoin(s.cfg.BondDenom, sdkmath.NewInt(1)),
),
addresscodec.NewBech32Codec("cosmos"),
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
fmt.Sprintf("--%s", flags.FlagOffline),
fmt.Sprintf("--%s=0", flags.FlagAccountNumber),
fmt.Sprintf("--%s=2", flags.FlagSequence),
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync),
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdkmath.NewInt(10))).String()),
fmt.Sprintf("--gas=%d", flags.DefaultGasLimit),
fmt.Sprintf("--%s=foobar", flags.FlagNote),
)
assert.NilError(tb, err)
var tr sdk.TxResponse
assert.NilError(tb, val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &tr))
assert.Equal(tb, uint32(0), tr.Code)
resp, err := cli.GetTxResponse(s.network, val.ClientCtx, tr.TxHash)
assert.NilError(tb, err)
s.txHeight = resp.Height
return s
}
func (s *E2EBenchmarkSuite) Close() {
s.network.Cleanup()
}
func mkTxBuilder(tb testing.TB, s *E2EBenchmarkSuite) client.TxBuilder {
tb.Helper()
val := s.network.Validators[0]
assert.NilError(tb, s.network.WaitForNextBlock())
// prepare txBuilder with msg
txBuilder := val.ClientCtx.TxConfig.NewTxBuilder()
feeAmount := sdk.Coins{sdk.NewInt64Coin(s.cfg.BondDenom, 10)}
gasLimit := testdata.NewTestGasLimit()
assert.NilError(tb,
txBuilder.SetMsgs(&banktypes.MsgSend{
FromAddress: val.Address.String(),
ToAddress: val.Address.String(),
Amount: sdk.Coins{sdk.NewInt64Coin(s.cfg.BondDenom, 10)},
}),
)
txBuilder.SetFeeAmount(feeAmount)
txBuilder.SetGasLimit(gasLimit)
txBuilder.SetMemo("foobar")
// setup txFactory
txFactory := clienttx.Factory{}.
WithChainID(val.ClientCtx.ChainID).
WithKeybase(val.ClientCtx.Keyring).
WithTxConfig(val.ClientCtx.TxConfig).
WithSignMode(signing.SignMode_SIGN_MODE_DIRECT)
// Sign Tx.
err := authclient.SignTx(txFactory, val.ClientCtx, val.Moniker, txBuilder, false, true)
assert.NilError(tb, err)
return txBuilder
}