Skip to content

Commit 4f959c1

Browse files
authored
Merge branch 'main' into julien/v3-debug-module-by-hash
2 parents b60f3a3 + 54a5a20 commit 4f959c1

6 files changed

Lines changed: 172 additions & 25 deletions

File tree

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ require (
77
cosmossdk.io/math v1.5.0
88
github.com/celestiaorg/blobstream-contracts/v3 v3.1.0
99
github.com/celestiaorg/go-square v1.1.1
10-
github.com/celestiaorg/go-square/v2 v2.1.0
10+
github.com/celestiaorg/go-square/v2 v2.2.0
1111
github.com/celestiaorg/knuu v0.16.3
1212
github.com/celestiaorg/nmt v0.23.0
1313
github.com/celestiaorg/rsmt2d v0.14.0

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -324,8 +324,8 @@ github.com/celestiaorg/cosmos-sdk v1.28.0-sdk-v0.46.16 h1:i0soIBrwjrjvQGZaOG3e7W
324324
github.com/celestiaorg/cosmos-sdk v1.28.0-sdk-v0.46.16/go.mod h1:8Wvxp2fu7ZMEz2sJVlfZiCqgdjLMNVosuE1O0X2yR5c=
325325
github.com/celestiaorg/go-square v1.1.1 h1:Cy3p8WVspVcyOqHM8BWFuuYPwMitO1pYGe+ImILFZRA=
326326
github.com/celestiaorg/go-square v1.1.1/go.mod h1:1EXMErhDrWJM8B8V9hN7dqJ2kUTClfwdqMOmF9yQUa0=
327-
github.com/celestiaorg/go-square/v2 v2.1.0 h1:ECIvYEeHIWiIJGDCJxQNtzqm5DmnBly7XGhSpLsl+Lw=
328-
github.com/celestiaorg/go-square/v2 v2.1.0/go.mod h1:n3ztrh8CBjWOD6iWYMo3pPOlQIgzLK9yrnqMPcNo6g8=
327+
github.com/celestiaorg/go-square/v2 v2.2.0 h1:zJnUxCYc65S8FgUfVpyG/osDcsnjzo/JSXw/Uwn8zp4=
328+
github.com/celestiaorg/go-square/v2 v2.2.0/go.mod h1:j8kQUqJLYtcvCQMQV6QjEhUdaF7rBTXF74g8LbkR0Co=
329329
github.com/celestiaorg/ibc-go/v6 v6.2.5 h1:mUmZptulQGsGgyBqmdBeSCPQtMu0aFMJstgvKhUdqy4=
330330
github.com/celestiaorg/ibc-go/v6 v6.2.5/go.mod h1:XLsARy4Y7+GtAqzMcxNdlQf6lx+ti1e8KcMGv5NIK7A=
331331
github.com/celestiaorg/knuu v0.16.3 h1:ORmcBoSW+67iPKF0yIGKwWMefphk5hvy08KklmCT0aw=

x/blob/ante/ante.go

Lines changed: 51 additions & 15 deletions
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

Lines changed: 47 additions & 0 deletions
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 {

x/blob/ante/blob_share_decorator.go

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
package ante
22

33
import (
4+
"math"
5+
6+
"cosmossdk.io/errors"
47
"github.com/celestiaorg/celestia-app/v3/pkg/appconsts"
58
v1 "github.com/celestiaorg/celestia-app/v3/pkg/appconsts/v1"
69
blobtypes "github.com/celestiaorg/celestia-app/v3/x/blob/types"
710
"github.com/celestiaorg/go-square/v2/share"
8-
9-
"cosmossdk.io/errors"
1011
sdk "github.com/cosmos/cosmos-sdk/types"
12+
"github.com/cosmos/cosmos-sdk/x/authz"
1113
)
1214

1315
// BlobShareDecorator helps to prevent a PFB from being included in a block but
@@ -33,16 +35,42 @@ func (d BlobShareDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool
3335
return next(ctx, tx, simulate)
3436
}
3537

38+
txBytes := ctx.TxBytes()
39+
if len(txBytes) > math.MaxUint32 {
40+
return ctx, errors.Wrapf(blobtypes.ErrBlobsTooLarge, "the tx size %d exceeds the max uint32", txBytes)
41+
}
42+
txSize := uint32(len(txBytes))
3643
maxBlobShares := d.getMaxBlobShares(ctx)
37-
for _, m := range tx.GetMsgs() {
44+
45+
err := d.validateMsgs(tx.GetMsgs(), txSize, maxBlobShares)
46+
if err != nil {
47+
return ctx, err
48+
}
49+
50+
return next(ctx, tx, simulate)
51+
}
52+
53+
func (d BlobShareDecorator) validateMsgs(msgs []sdk.Msg, txSize uint32, maxBlobShares int) error {
54+
for _, m := range msgs {
55+
if execMsg, ok := m.(*authz.MsgExec); ok {
56+
// Recursively look for PFBs in nested authz messages.
57+
nestedMsgs, err := execMsg.GetMessages()
58+
if err != nil {
59+
return err
60+
}
61+
err = d.validateMsgs(nestedMsgs, txSize, maxBlobShares)
62+
if err != nil {
63+
return err
64+
}
65+
}
66+
3867
if pfb, ok := m.(*blobtypes.MsgPayForBlobs); ok {
39-
if sharesNeeded := getSharesNeeded(uint32(len(ctx.TxBytes())), pfb.BlobSizes); sharesNeeded > maxBlobShares {
40-
return ctx, errors.Wrapf(blobtypes.ErrBlobsTooLarge, "the number of shares occupied by blobs in this MsgPayForBlobs %d exceeds the max number of shares available for blob data %d", sharesNeeded, maxBlobShares)
68+
if sharesNeeded := getSharesNeeded(txSize, pfb.BlobSizes); sharesNeeded > maxBlobShares {
69+
return errors.Wrapf(blobtypes.ErrBlobsTooLarge, "the number of shares occupied by blobs in this MsgPayForBlobs %d exceeds the max number of shares available for blob data %d", sharesNeeded, maxBlobShares)
4170
}
4271
}
4372
}
44-
45-
return next(ctx, tx, simulate)
73+
return nil
4674
}
4775

4876
// getMaxBlobShares returns the max the number of shares available for blob data.

x/blob/ante/blob_share_decorator_test.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
package ante_test
22

33
import (
4+
"math"
45
"testing"
56

67
"github.com/celestiaorg/celestia-app/v3/app"
78
"github.com/celestiaorg/celestia-app/v3/app/encoding"
9+
"github.com/celestiaorg/celestia-app/v3/pkg/appconsts"
810
v1 "github.com/celestiaorg/celestia-app/v3/pkg/appconsts/v1"
911
v2 "github.com/celestiaorg/celestia-app/v3/pkg/appconsts/v2"
1012
"github.com/celestiaorg/celestia-app/v3/pkg/user"
@@ -16,6 +18,7 @@ import (
1618
"github.com/celestiaorg/go-square/v2/share"
1719
blobtx "github.com/celestiaorg/go-square/v2/tx"
1820
sdk "github.com/cosmos/cosmos-sdk/types"
21+
"github.com/cosmos/cosmos-sdk/x/authz"
1922
"github.com/stretchr/testify/assert"
2023
"github.com/stretchr/testify/require"
2124
tmrand "github.com/tendermint/tendermint/libs/rand"
@@ -166,6 +169,39 @@ func TestBlobShareDecorator(t *testing.T) {
166169
}
167170
}
168171

172+
func TestBlobShareDecoratorWithMsgExec(t *testing.T) {
173+
decorator := ante.NewBlobShareDecorator(mockBlobKeeper{})
174+
ctx := sdk.Context{}.
175+
WithIsCheckTx(true).
176+
WithBlockHeader(tmproto.Header{Version: version.Consensus{App: appconsts.LatestVersion}})
177+
178+
txConfig := encoding.MakeConfig(app.ModuleEncodingRegisters...).TxConfig
179+
txBuilder := txConfig.NewTxBuilder()
180+
// Create a tx with a MsgExec that contains a MsgPayForBlobs with a huge
181+
// blob that can not fit in a square.
182+
msgExec := authz.NewMsgExec(sdk.AccAddress{}, []sdk.Msg{
183+
&blob.MsgPayForBlobs{
184+
Signer: "celestia...",
185+
BlobSizes: []uint32{uint32(math.MaxUint32)},
186+
},
187+
})
188+
require.NoError(t, txBuilder.SetMsgs(&msgExec))
189+
tx := txBuilder.GetTx()
190+
191+
_, err := decorator.AnteHandle(ctx, tx, false, mockNext)
192+
assert.Error(t, err)
193+
assert.ErrorIs(t, err, blob.ErrBlobsTooLarge)
194+
195+
// Create a MsgExec that wraps a MsgExec that contains a MsgPayForBlobs with a huge
196+
// blob that can not fit in a square.
197+
nestedMsgExec := authz.NewMsgExec(sdk.AccAddress{}, []sdk.Msg{&msgExec})
198+
require.NoError(t, txBuilder.SetMsgs(&nestedMsgExec))
199+
tx = txBuilder.GetTx()
200+
_, err = decorator.AnteHandle(ctx, tx, false, mockNext)
201+
assert.Error(t, err)
202+
assert.ErrorIs(t, err, blob.ErrBlobsTooLarge)
203+
}
204+
169205
func mockNext(ctx sdk.Context, _ sdk.Tx, _ bool) (sdk.Context, error) {
170206
return ctx, nil
171207
}

0 commit comments

Comments
 (0)