github.com/cosmos/cosmos-sdk@v0.50.10/baseapp/block_gas_test.go (about) 1 package baseapp_test 2 3 import ( 4 "context" 5 "math" 6 "testing" 7 8 abci "github.com/cometbft/cometbft/abci/types" 9 cmtjson "github.com/cometbft/cometbft/libs/json" 10 dbm "github.com/cosmos/cosmos-db" 11 "github.com/stretchr/testify/require" 12 13 "cosmossdk.io/depinject" 14 "cosmossdk.io/log" 15 sdkmath "cosmossdk.io/math" 16 store "cosmossdk.io/store/types" 17 18 baseapptestutil "github.com/cosmos/cosmos-sdk/baseapp/testutil" 19 "github.com/cosmos/cosmos-sdk/client" 20 "github.com/cosmos/cosmos-sdk/client/tx" 21 "github.com/cosmos/cosmos-sdk/codec" 22 codectypes "github.com/cosmos/cosmos-sdk/codec/types" 23 cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" 24 "github.com/cosmos/cosmos-sdk/runtime" 25 "github.com/cosmos/cosmos-sdk/testutil/configurator" 26 simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" 27 "github.com/cosmos/cosmos-sdk/testutil/testdata" 28 sdk "github.com/cosmos/cosmos-sdk/types" 29 sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" 30 txtypes "github.com/cosmos/cosmos-sdk/types/tx" 31 "github.com/cosmos/cosmos-sdk/types/tx/signing" 32 authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" 33 xauthsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" 34 bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" 35 banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" 36 minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" 37 ) 38 39 var blockMaxGas = uint64(simtestutil.DefaultConsensusParams.Block.MaxGas) 40 41 type BlockGasImpl struct { 42 panicTx bool 43 gasToConsume uint64 44 key store.StoreKey 45 } 46 47 func (m BlockGasImpl) Set(ctx context.Context, msg *baseapptestutil.MsgKeyValue) (*baseapptestutil.MsgCreateKeyValueResponse, error) { 48 sdkCtx := sdk.UnwrapSDKContext(ctx) 49 sdkCtx.KVStore(m.key).Set(msg.Key, msg.Value) 50 sdkCtx.GasMeter().ConsumeGas(m.gasToConsume, "TestMsg") 51 if m.panicTx { 52 panic("panic in tx execution") 53 } 54 return &baseapptestutil.MsgCreateKeyValueResponse{}, nil 55 } 56 57 func TestBaseApp_BlockGas(t *testing.T) { 58 testcases := []struct { 59 name string 60 gasToConsume uint64 // gas to consume in the msg execution 61 panicTx bool // panic explicitly in tx execution 62 expErr bool 63 }{ 64 {"less than block gas meter", 10, false, false}, 65 {"more than block gas meter", blockMaxGas, false, true}, 66 {"more than block gas meter", uint64(float64(blockMaxGas) * 1.2), false, true}, 67 {"consume MaxUint64", math.MaxUint64, true, true}, 68 {"consume MaxGasWanted", txtypes.MaxGasWanted, false, true}, 69 {"consume block gas when panicked", 10, true, true}, 70 } 71 72 for _, tc := range testcases { 73 var ( 74 bankKeeper bankkeeper.Keeper 75 accountKeeper authkeeper.AccountKeeper 76 appBuilder *runtime.AppBuilder 77 txConfig client.TxConfig 78 cdc codec.Codec 79 interfaceRegistry codectypes.InterfaceRegistry 80 err error 81 ) 82 83 err = depinject.Inject( 84 depinject.Configs( 85 configurator.NewAppConfig( 86 configurator.AuthModule(), 87 configurator.TxModule(), 88 configurator.ParamsModule(), 89 configurator.ConsensusModule(), 90 configurator.BankModule(), 91 configurator.StakingModule(), 92 ), 93 depinject.Supply(log.NewNopLogger()), 94 ), 95 &bankKeeper, 96 &accountKeeper, 97 &interfaceRegistry, 98 &txConfig, 99 &cdc, 100 &appBuilder) 101 require.NoError(t, err) 102 103 bapp := appBuilder.Build(dbm.NewMemDB(), nil) 104 err = bapp.Load(true) 105 require.NoError(t, err) 106 107 t.Run(tc.name, func(t *testing.T) { 108 baseapptestutil.RegisterInterfaces(interfaceRegistry) 109 baseapptestutil.RegisterKeyValueServer(bapp.MsgServiceRouter(), BlockGasImpl{ 110 panicTx: tc.panicTx, 111 gasToConsume: tc.gasToConsume, 112 key: bapp.UnsafeFindStoreKey(banktypes.ModuleName), 113 }) 114 115 genState := GenesisStateWithSingleValidator(t, cdc, appBuilder) 116 stateBytes, err := cmtjson.MarshalIndent(genState, "", " ") 117 require.NoError(t, err) 118 bapp.InitChain(&abci.RequestInitChain{ 119 Validators: []abci.ValidatorUpdate{}, 120 ConsensusParams: simtestutil.DefaultConsensusParams, 121 AppStateBytes: stateBytes, 122 }) 123 124 ctx := bapp.NewContext(false) 125 126 // tx fee 127 feeCoin := sdk.NewCoin("atom", sdkmath.NewInt(150)) 128 feeAmount := sdk.NewCoins(feeCoin) 129 130 // test account and fund 131 priv1, _, addr1 := testdata.KeyTestPubAddr() 132 err = bankKeeper.MintCoins(ctx, minttypes.ModuleName, feeAmount) 133 require.NoError(t, err) 134 err = bankKeeper.SendCoinsFromModuleToAccount(ctx, minttypes.ModuleName, addr1, feeAmount) 135 require.NoError(t, err) 136 require.Equal(t, feeCoin.Amount, bankKeeper.GetBalance(ctx, addr1, feeCoin.Denom).Amount) 137 seq := accountKeeper.GetAccount(ctx, addr1).GetSequence() 138 require.Equal(t, uint64(0), seq) 139 140 // msg and signatures 141 msg := &baseapptestutil.MsgKeyValue{ 142 Key: []byte("ok"), 143 Value: []byte("ok"), 144 Signer: addr1.String(), 145 } 146 147 txBuilder := txConfig.NewTxBuilder() 148 149 require.NoError(t, txBuilder.SetMsgs(msg)) 150 txBuilder.SetFeeAmount(feeAmount) 151 txBuilder.SetGasLimit(uint64(simtestutil.DefaultConsensusParams.Block.MaxGas)) 152 153 senderAccountNumber := accountKeeper.GetAccount(ctx, addr1).GetAccountNumber() 154 privs, accNums, accSeqs := []cryptotypes.PrivKey{priv1}, []uint64{senderAccountNumber}, []uint64{0} 155 _, txBytes, err := createTestTx(txConfig, txBuilder, privs, accNums, accSeqs, ctx.ChainID()) 156 require.NoError(t, err) 157 158 rsp, err := bapp.FinalizeBlock(&abci.RequestFinalizeBlock{Height: 1, Txs: [][]byte{txBytes}}) 159 require.NoError(t, err) 160 161 // check result 162 ctx = bapp.GetContextForFinalizeBlock(txBytes) 163 okValue := ctx.KVStore(bapp.UnsafeFindStoreKey(banktypes.ModuleName)).Get([]byte("ok")) 164 165 if tc.expErr { 166 if tc.panicTx { 167 require.Equal(t, sdkerrors.ErrPanic.ABCICode(), rsp.TxResults[0].Code) 168 } else { 169 require.Equal(t, sdkerrors.ErrOutOfGas.ABCICode(), rsp.TxResults[0].Code) 170 } 171 require.Empty(t, okValue) 172 } else { 173 require.Equal(t, uint32(0), rsp.TxResults[0].Code) 174 require.Equal(t, []byte("ok"), okValue) 175 } 176 // check block gas is always consumed 177 baseGas := uint64(57504) // baseGas is the gas consumed before tx msg 178 expGasConsumed := addUint64Saturating(tc.gasToConsume, baseGas) 179 if expGasConsumed > uint64(simtestutil.DefaultConsensusParams.Block.MaxGas) { 180 // capped by gasLimit 181 expGasConsumed = uint64(simtestutil.DefaultConsensusParams.Block.MaxGas) 182 } 183 require.Equal(t, int(expGasConsumed), int(ctx.BlockGasMeter().GasConsumed())) 184 // tx fee is always deducted 185 require.Equal(t, int64(0), bankKeeper.GetBalance(ctx, addr1, feeCoin.Denom).Amount.Int64()) 186 // sender's sequence is always increased 187 seq = accountKeeper.GetAccount(ctx, addr1).GetSequence() 188 require.NoError(t, err) 189 require.Equal(t, uint64(1), seq) 190 }) 191 } 192 } 193 194 func createTestTx(txConfig client.TxConfig, txBuilder client.TxBuilder, privs []cryptotypes.PrivKey, accNums, accSeqs []uint64, chainID string) (xauthsigning.Tx, []byte, error) { 195 defaultSignMode, err := xauthsigning.APISignModeToInternal(txConfig.SignModeHandler().DefaultMode()) 196 if err != nil { 197 return nil, nil, err 198 } 199 // First round: we gather all the signer infos. We use the "set empty 200 // signature" hack to do that. 201 var sigsV2 []signing.SignatureV2 202 for i, priv := range privs { 203 sigV2 := signing.SignatureV2{ 204 PubKey: priv.PubKey(), 205 Data: &signing.SingleSignatureData{ 206 SignMode: defaultSignMode, 207 Signature: nil, 208 }, 209 Sequence: accSeqs[i], 210 } 211 212 sigsV2 = append(sigsV2, sigV2) 213 } 214 err = txBuilder.SetSignatures(sigsV2...) 215 if err != nil { 216 return nil, nil, err 217 } 218 219 // Second round: all signer infos are set, so each signer can sign. 220 sigsV2 = []signing.SignatureV2{} 221 for i, priv := range privs { 222 signerData := xauthsigning.SignerData{ 223 Address: sdk.AccAddress(priv.PubKey().Bytes()).String(), 224 ChainID: chainID, 225 AccountNumber: accNums[i], 226 Sequence: accSeqs[i], 227 PubKey: priv.PubKey(), 228 } 229 sigV2, err := tx.SignWithPrivKey( 230 context.TODO(), defaultSignMode, signerData, 231 txBuilder, priv, txConfig, accSeqs[i]) 232 if err != nil { 233 return nil, nil, err 234 } 235 236 sigsV2 = append(sigsV2, sigV2) 237 } 238 err = txBuilder.SetSignatures(sigsV2...) 239 if err != nil { 240 return nil, nil, err 241 } 242 243 txBytes, err := txConfig.TxEncoder()(txBuilder.GetTx()) 244 if err != nil { 245 return nil, nil, err 246 } 247 248 return txBuilder.GetTx(), txBytes, nil 249 } 250 251 func addUint64Saturating(a, b uint64) uint64 { 252 if math.MaxUint64-a < b { 253 return math.MaxUint64 254 } 255 256 return a + b 257 }