github.com/Finschia/finschia-sdk@v0.48.1/baseapp/block_gas_test.go (about)

     1  package baseapp_test
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"math"
     7  	"testing"
     8  
     9  	"github.com/stretchr/testify/require"
    10  	abci "github.com/tendermint/tendermint/abci/types"
    11  	tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
    12  	dbm "github.com/tendermint/tm-db"
    13  
    14  	ocabci "github.com/Finschia/ostracon/abci/types"
    15  	"github.com/Finschia/ostracon/libs/log"
    16  
    17  	"github.com/Finschia/finschia-sdk/baseapp"
    18  	"github.com/Finschia/finschia-sdk/client"
    19  	"github.com/Finschia/finschia-sdk/client/tx"
    20  	cryptotypes "github.com/Finschia/finschia-sdk/crypto/types"
    21  	"github.com/Finschia/finschia-sdk/simapp"
    22  	"github.com/Finschia/finschia-sdk/testutil/testdata"
    23  	sdk "github.com/Finschia/finschia-sdk/types"
    24  	sdkerrors "github.com/Finschia/finschia-sdk/types/errors"
    25  	txtypes "github.com/Finschia/finschia-sdk/types/tx"
    26  	"github.com/Finschia/finschia-sdk/types/tx/signing"
    27  	xauthsigning "github.com/Finschia/finschia-sdk/x/auth/signing"
    28  	banktypes "github.com/Finschia/finschia-sdk/x/bank/types"
    29  	minttypes "github.com/Finschia/finschia-sdk/x/mint/types"
    30  )
    31  
    32  var blockMaxGas = uint64(simapp.DefaultConsensusParams.Block.MaxGas)
    33  
    34  func TestBaseApp_BlockGas(t *testing.T) {
    35  	testcases := []struct {
    36  		name         string
    37  		gasToConsume uint64 // gas to consume in the msg execution
    38  		panicTx      bool   // panic explicitly in tx execution
    39  		expErr       bool
    40  	}{
    41  		{"less than block gas meter", 10, false, false},
    42  		{"more than block gas meter", blockMaxGas, false, true},
    43  		{"more than block gas meter", uint64(float64(blockMaxGas) * 1.2), false, true},
    44  		{"consume MaxUint64", math.MaxUint64, false, true},
    45  		{"consume MaxGasWanted", txtypes.MaxGasWanted, false, true},
    46  		{"consume block gas when paniced", 10, true, true},
    47  	}
    48  	for _, tc := range testcases {
    49  		t.Run(tc.name, func(t *testing.T) {
    50  			var app *simapp.SimApp
    51  			routerOpt := func(bapp *baseapp.BaseApp) {
    52  				route := (&testdata.TestMsg{}).Route()
    53  				bapp.Router().AddRoute(sdk.NewRoute(route, func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) {
    54  					_, ok := msg.(*testdata.TestMsg)
    55  					if !ok {
    56  						return &sdk.Result{}, fmt.Errorf("Wrong Msg type, expected %T, got %T", (*testdata.TestMsg)(nil), msg)
    57  					}
    58  					ctx.KVStore(app.GetKey(banktypes.ModuleName)).Set([]byte("ok"), []byte("ok"))
    59  					ctx.GasMeter().ConsumeGas(tc.gasToConsume, "TestMsg")
    60  					if tc.panicTx {
    61  						panic("panic in tx execution")
    62  					}
    63  					return &sdk.Result{}, nil
    64  				}))
    65  			}
    66  			encCfg := simapp.MakeTestEncodingConfig()
    67  			encCfg.Amino.RegisterConcrete(&testdata.TestMsg{}, "testdata.TestMsg", nil)
    68  			encCfg.InterfaceRegistry.RegisterImplementations((*sdk.Msg)(nil),
    69  				&testdata.TestMsg{},
    70  			)
    71  			app = simapp.NewSimApp(log.NewNopLogger(), dbm.NewMemDB(), nil, true, map[int64]bool{}, "", 0, encCfg, simapp.EmptyAppOptions{}, routerOpt)
    72  			genState := simapp.NewDefaultGenesisState(encCfg.Marshaler)
    73  			stateBytes, err := json.MarshalIndent(genState, "", " ")
    74  			require.NoError(t, err)
    75  			app.InitChain(abci.RequestInitChain{
    76  				Validators:      []abci.ValidatorUpdate{},
    77  				ConsensusParams: simapp.DefaultConsensusParams,
    78  				AppStateBytes:   stateBytes,
    79  			})
    80  
    81  			ctx := app.NewContext(false, tmproto.Header{})
    82  
    83  			// tx fee
    84  			feeCoin := sdk.NewCoin("atom", sdk.NewInt(150))
    85  			feeAmount := sdk.NewCoins(feeCoin)
    86  
    87  			// test account and fund
    88  			priv1, _, addr1 := testdata.KeyTestPubAddr()
    89  			err = app.BankKeeper.MintCoins(ctx, minttypes.ModuleName, feeAmount)
    90  			require.NoError(t, err)
    91  			err = app.BankKeeper.SendCoinsFromModuleToAccount(ctx, minttypes.ModuleName, addr1, feeAmount)
    92  			require.NoError(t, err)
    93  			require.Equal(t, feeCoin.Amount, app.BankKeeper.GetBalance(ctx, addr1, feeCoin.Denom).Amount)
    94  			seq, _ := app.AccountKeeper.GetSequence(ctx, addr1)
    95  			require.Equal(t, uint64(0), seq)
    96  
    97  			// msg and signatures
    98  			msg := testdata.NewTestMsg(addr1)
    99  
   100  			txBuilder := encCfg.TxConfig.NewTxBuilder()
   101  			require.NoError(t, txBuilder.SetMsgs(msg))
   102  			txBuilder.SetFeeAmount(feeAmount)
   103  			txBuilder.SetGasLimit(txtypes.MaxGasWanted) // tx validation checks that gasLimit can't be bigger than this
   104  
   105  			privs, accNums, accSeqs := []cryptotypes.PrivKey{priv1}, []uint64{6}, []uint64{0}
   106  			_, txBytes, err := createTestTx(encCfg.TxConfig, txBuilder, privs, accNums, accSeqs, ctx.ChainID())
   107  			require.NoError(t, err)
   108  
   109  			app.BeginBlock(ocabci.RequestBeginBlock{Header: tmproto.Header{Height: 1}})
   110  			rsp := app.DeliverTx(abci.RequestDeliverTx{Tx: txBytes})
   111  
   112  			// check result
   113  			ctx = app.GetContextForDeliverTx(txBytes)
   114  			okValue := ctx.KVStore(app.GetKey(banktypes.ModuleName)).Get([]byte("ok"))
   115  
   116  			if tc.expErr {
   117  				if tc.panicTx {
   118  					require.Equal(t, sdkerrors.ErrPanic.ABCICode(), rsp.Code)
   119  				} else {
   120  					require.Equal(t, sdkerrors.ErrOutOfGas.ABCICode(), rsp.Code)
   121  				}
   122  				require.Empty(t, okValue)
   123  			} else {
   124  				require.Equal(t, uint32(0), rsp.Code)
   125  				require.Equal(t, []byte("ok"), okValue)
   126  			}
   127  			// check block gas is always consumed
   128  			baseGas := uint64(37352) // baseGas is the gas consumed before tx msg
   129  			expGasConsumed := addUint64Saturating(tc.gasToConsume, baseGas)
   130  			if expGasConsumed > txtypes.MaxGasWanted {
   131  				// capped by gasLimit
   132  				expGasConsumed = txtypes.MaxGasWanted
   133  			}
   134  			require.Equal(t, expGasConsumed, ctx.BlockGasMeter().GasConsumed())
   135  			// tx fee is always deducted
   136  			require.Equal(t, int64(0), app.BankKeeper.GetBalance(ctx, addr1, feeCoin.Denom).Amount.Int64())
   137  			// sender's sequence is always increased
   138  			seq, err = app.AccountKeeper.GetSequence(ctx, addr1)
   139  			require.NoError(t, err)
   140  			require.Equal(t, uint64(1), seq)
   141  		})
   142  	}
   143  }
   144  
   145  func createTestTx(txConfig client.TxConfig, txBuilder client.TxBuilder, privs []cryptotypes.PrivKey, accNums []uint64, accSeqs []uint64, chainID string) (xauthsigning.Tx, []byte, error) {
   146  	// First round: we gather all the signer infos. We use the "set empty
   147  	// signature" hack to do that.
   148  	var sigsV2 []signing.SignatureV2
   149  	for i, priv := range privs {
   150  		sigV2 := signing.SignatureV2{
   151  			PubKey: priv.PubKey(),
   152  			Data: &signing.SingleSignatureData{
   153  				SignMode:  txConfig.SignModeHandler().DefaultMode(),
   154  				Signature: nil,
   155  			},
   156  			Sequence: accSeqs[i],
   157  		}
   158  
   159  		sigsV2 = append(sigsV2, sigV2)
   160  	}
   161  	err := txBuilder.SetSignatures(sigsV2...)
   162  	if err != nil {
   163  		return nil, nil, err
   164  	}
   165  
   166  	// Second round: all signer infos are set, so each signer can sign.
   167  	sigsV2 = []signing.SignatureV2{}
   168  	for i, priv := range privs {
   169  		signerData := xauthsigning.SignerData{
   170  			ChainID:       chainID,
   171  			AccountNumber: accNums[i],
   172  			Sequence:      accSeqs[i],
   173  		}
   174  		sigV2, err := tx.SignWithPrivKey(
   175  			txConfig.SignModeHandler().DefaultMode(), signerData,
   176  			txBuilder, priv, txConfig, accSeqs[i])
   177  		if err != nil {
   178  			return nil, nil, err
   179  		}
   180  
   181  		sigsV2 = append(sigsV2, sigV2)
   182  	}
   183  	err = txBuilder.SetSignatures(sigsV2...)
   184  	if err != nil {
   185  		return nil, nil, err
   186  	}
   187  
   188  	txBytes, err := txConfig.TxEncoder()(txBuilder.GetTx())
   189  	if err != nil {
   190  		return nil, nil, err
   191  	}
   192  
   193  	return txBuilder.GetTx(), txBytes, nil
   194  }
   195  
   196  func addUint64Saturating(a, b uint64) uint64 {
   197  	if math.MaxUint64-a < b {
   198  		return math.MaxUint64
   199  	}
   200  
   201  	return a + b
   202  }