Skip to content

Commit 7080af9

Browse files
fix: MinGasPFB decorator check for nested authz messages (backport #4465) (#4468)
Previously the MinGasPFB decorator was not looking inside authz `MsgExec`s.<hr>This is an automatic backport of pull request #4465 done by [Mergify](https://mergify.com). Co-authored-by: Rootul P <[email protected]>
1 parent 13d1c7e commit 7080af9

File tree

2 files changed

+98
-15
lines changed

2 files changed

+98
-15
lines changed

x/blob/ante/ante.go

+51-15
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"cosmossdk.io/errors"
99
sdk "github.com/cosmos/cosmos-sdk/types"
1010
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
11+
"github.com/cosmos/cosmos-sdk/x/authz"
1112
)
1213

1314
// MinGasPFBDecorator helps to prevent a PFB from being included in a block
@@ -29,27 +30,62 @@ func (d MinGasPFBDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool
2930
return next(ctx, tx, simulate)
3031
}
3132

32-
var gasPerByte uint32
33+
// Skip gas checks during genesis initialization
34+
if ctx.BlockHeight() == 0 {
35+
return next(ctx, tx, simulate)
36+
}
37+
38+
gasPerByte := d.getGasPerByte(ctx)
3339
txGas := ctx.GasMeter().GasRemaining()
34-
for _, m := range tx.GetMsgs() {
35-
// NOTE: here we assume only one PFB per transaction
36-
if pfb, ok := m.(*types.MsgPayForBlobs); ok {
37-
if gasPerByte == 0 {
38-
if ctx.BlockHeader().Version.App <= v2.Version {
39-
// lazily fetch the gas per byte param
40-
gasPerByte = d.k.GasPerBlobByte(ctx)
41-
} else {
42-
gasPerByte = appconsts.GasPerBlobByte(ctx.BlockHeader().Version.App)
43-
}
40+
err := d.validatePFBHasEnoughGas(tx.GetMsgs(), gasPerByte, txGas)
41+
if err != nil {
42+
return ctx, err
43+
}
44+
45+
return next(ctx, tx, simulate)
46+
}
47+
48+
// validatePFBHasEnoughGas iterates through all the msgs and nested msgs to find
49+
// a MsgPayForBlobs. If found, it validates that the txGas is enough to pay for
50+
// the blobs.
51+
func (d MinGasPFBDecorator) validatePFBHasEnoughGas(msgs []sdk.Msg, gasPerByte uint32, txGas uint64) error {
52+
for _, m := range msgs {
53+
if execMsg, ok := m.(*authz.MsgExec); ok {
54+
// Recursively look for PFBs in nested authz messages.
55+
nestedMsgs, err := execMsg.GetMessages()
56+
if err != nil {
57+
return err
4458
}
45-
gasToConsume := pfb.Gas(gasPerByte)
46-
if gasToConsume > txGas {
47-
return ctx, errors.Wrapf(sdkerrors.ErrInsufficientFee, "not enough gas to pay for blobs (minimum: %d, got: %d)", gasToConsume, txGas)
59+
err = d.validatePFBHasEnoughGas(nestedMsgs, gasPerByte, txGas)
60+
if err != nil {
61+
return err
62+
}
63+
}
64+
if pfb, ok := m.(*types.MsgPayForBlobs); ok {
65+
err := validateEnoughGas(pfb, gasPerByte, txGas)
66+
if err != nil {
67+
return err
4868
}
4969
}
5070
}
71+
return nil
72+
}
5173

52-
return next(ctx, tx, simulate)
74+
func (d MinGasPFBDecorator) getGasPerByte(ctx sdk.Context) uint32 {
75+
if ctx.BlockHeader().Version.App <= v2.Version {
76+
return d.k.GasPerBlobByte(ctx)
77+
}
78+
return appconsts.GasPerBlobByte(ctx.BlockHeader().Version.App)
79+
}
80+
81+
// validateEnoughGas returns an error if the gas needed to pay for the blobs is
82+
// greater than the txGas.
83+
func validateEnoughGas(msg *types.MsgPayForBlobs, gasPerByte uint32, txGas uint64) error {
84+
gasToConsume := msg.Gas(gasPerByte)
85+
if gasToConsume > txGas {
86+
return errors.Wrapf(sdkerrors.ErrInsufficientFee, "not enough gas to pay for blobs (minimum: %d, got: %d)", gasToConsume, txGas)
87+
}
88+
return nil
5389
}
5490

5591
type BlobKeeper interface {

x/blob/ante/ante_test.go

+47
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package ante_test
22

33
import (
44
"fmt"
5+
"math"
56
"testing"
67

78
"github.com/celestiaorg/celestia-app/v3/app"
@@ -12,6 +13,8 @@ import (
1213
blob "github.com/celestiaorg/celestia-app/v3/x/blob/types"
1314
"github.com/celestiaorg/go-square/v2/share"
1415
sdk "github.com/cosmos/cosmos-sdk/types"
16+
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
17+
"github.com/cosmos/cosmos-sdk/x/authz"
1518
"github.com/stretchr/testify/require"
1619
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
1720
"github.com/tendermint/tendermint/proto/tendermint/version"
@@ -124,6 +127,7 @@ func TestPFBAnteHandler(t *testing.T) {
124127
Version: version.Consensus{
125128
App: currentVersion,
126129
},
130+
Height: 1,
127131
}, true, nil).WithGasMeter(sdk.NewGasMeter(uint64(tc.txGas(gasPerBlobByte)))).WithIsCheckTx(true)
128132

129133
ctx.GasMeter().ConsumeGas(tc.gasConsumed, "test")
@@ -141,6 +145,49 @@ func TestPFBAnteHandler(t *testing.T) {
141145
}
142146
}
143147

148+
// TestMinGasPFBDecoratorWithMsgExec tests that the MinGasPFBDecorator rejects a
149+
// MsgExec that contains a MsgExec with a MsgPayForBlob where the MsgPayForBlob
150+
// gas cost is greater than the tx's gas limit.
151+
func TestMinGasPFBDecoratorWithMsgExec(t *testing.T) {
152+
anteHandler := ante.NewMinGasPFBDecorator(mockBlobKeeper{})
153+
txConfig := encoding.MakeConfig(app.ModuleEncodingRegisters...).TxConfig
154+
155+
// Create a context with a gas meter with a high gas limit
156+
gasLimit := uint64(100_000_000)
157+
ctx := sdk.NewContext(nil, tmproto.Header{
158+
Version: version.Consensus{
159+
App: appconsts.LatestVersion,
160+
},
161+
Height: 1,
162+
}, true, nil).WithGasMeter(sdk.NewGasMeter(gasLimit)).WithIsCheckTx(true)
163+
164+
// Build a tx with a MsgExec containing a MsgPayForBlobs with a huge gas cost
165+
txBuilder := txConfig.NewTxBuilder()
166+
msgExec := authz.NewMsgExec(sdk.AccAddress{}, []sdk.Msg{
167+
&blob.MsgPayForBlobs{
168+
Signer: "celestia...",
169+
BlobSizes: []uint32{uint32(math.MaxUint32)},
170+
},
171+
})
172+
require.NoError(t, txBuilder.SetMsgs(&msgExec))
173+
tx := txBuilder.GetTx()
174+
175+
// Run the ante handler
176+
_, err := anteHandler.AnteHandle(ctx, tx, false, mockNext)
177+
require.Error(t, err)
178+
require.ErrorIs(t, err, sdkerrors.ErrInsufficientFee)
179+
180+
// Create a MsgExec that wraps a MsgExec that contains a MsgPayForBlobs with a huge gas cost
181+
nestedMsgExec := authz.NewMsgExec(sdk.AccAddress{}, []sdk.Msg{&msgExec})
182+
require.NoError(t, txBuilder.SetMsgs(&nestedMsgExec))
183+
tx = txBuilder.GetTx()
184+
185+
// Run the ante handler
186+
_, err = anteHandler.AnteHandle(ctx, tx, false, mockNext)
187+
require.Error(t, err)
188+
require.ErrorIs(t, err, sdkerrors.ErrInsufficientFee)
189+
}
190+
144191
type mockBlobKeeper struct{}
145192

146193
func (mockBlobKeeper) GasPerBlobByte(_ sdk.Context) uint32 {

0 commit comments

Comments
 (0)