Repository: celestiaorg/celestia-app

Issue:
Decrease `UpgradeHeightDelay` on networks
## Context

https://celestia-team.slack.com/archives/C087852KEVC/p1741966964952959

## Problem

Currently the `UpgradeHeightDelay` is 7 days on Arabica, Mocha, and Mainnet Beta. This means it will take more than 3 weeks to deploy a new celestia-app major version across all networks. 

The UpgradeHeightDelay is useful to give some grace period to the 1/6 of voting power that haven't upgraded prior to the activation height. 

```mermaid
graph LR
  A[Create v4.0.0-arabica. Ask validators to upgrade binary & signal for v4] -- Wait until 5/6 voting power has signalled for v4--> B[Submit MsgTryUpgrade]
  B -- Wait UpgradeHeightDelay --> C[v4 activates on Arabica]
```

## Proposal

1. Arabica: decrease `UpgradeHeightDelay` from 7 days to 1 day
2. Mocha: decrease `UpgradeHeightDelay` from 7 days to 2 days
3. ~~Mainnet: decrease `UpgradeHeightDelay` from 7 days to 3 days~~

Note: this is consensus breaking and we're going to release it in a minor release. Nodes must upgrade to a release with this change _prior_ to the upgrade process starting on that network (specifically before the `MsgTryUpgrade` is submitted).

Hints from issue comments:
@rootulp: Revised this issue to decrease the upgrade height on all networks. cc: @cmwaters @evan-forbes @adlerjohn 

@cmwaters: Im not opposed to these changes. A week seems a long time even after 5/6 have already voted for the upgrade. Currently we allocate an entire month after the first RC to release a major upgrade which is quite a long duration. Paired with the multiplexer, I think it would be valuable to making major upgrades quicker to roll out @jcstein: I'm in favor of these changes to make our release and upgrade pipeline more efficient@adlerjohn: Also fine with these changes@rootulp: In core/app sprint planning we agreed to decrease the upgrade height delay on Arabica and Mocha but not on Mainnet. I updated the issue description.

Solution patch:
diff --git a/Makefile b/Makefile
index f56c986a..76d929e4 100644
--- a/Makefile
+++ b/Makefile
@@ -28,8 +28,6 @@ PACKAGE_NAME          := github.com/celestiaorg/celestia-app/v3
 GOLANG_CROSS_VERSION  ?= v1.23.5
 # Set this to override the max square size of the binary
 OVERRIDE_MAX_SQUARE_SIZE ?=
-# Set this to override the upgrade height delay of the binary
-OVERRIDE_UPGRADE_HEIGHT_DELAY ?=
 
 # process linker flags
 ldflags = -X github.com/cosmos/cosmos-sdk/version.Name=celestia-app \
@@ -37,7 +35,6 @@ ldflags = -X github.com/cosmos/cosmos-sdk/version.Name=celestia-app \
 		  -X github.com/cosmos/cosmos-sdk/version.Version=$(VERSION) \
 		  -X github.com/cosmos/cosmos-sdk/version.Commit=$(COMMIT) \
 		  -X github.com/celestiaorg/celestia-app/v3/pkg/appconsts.OverrideSquareSizeUpperBoundStr=$(OVERRIDE_MAX_SQUARE_SIZE) \
-		  -X github.com/celestiaorg/celestia-app/v3/pkg/appconsts.OverrideUpgradeHeightDelayStr=$(OVERRIDE_UPGRADE_HEIGHT_DELAY)
 
 BUILD_FLAGS := -tags "ledger" -ldflags '$(ldflags)'
 
diff --git a/docker/Dockerfile b/docker/Dockerfile
index 5e0aeca7..464e2d69 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -35,7 +35,6 @@ WORKDIR /celestia-app
 RUN uname -a &&\
     CGO_ENABLED=${CGO_ENABLED} GOOS=${TARGETOS} GOARCH=${TARGETARCH} \
     OVERRIDE_MAX_SQUARE_SIZE=${MAX_SQUARE_SIZE} \
-    OVERRIDE_UPGRADE_HEIGHT_DELAY=${UPGRADE_HEIGHT_DELAY} \
     make build
 
 # Stage 2: Create a minimal image to run the celestia-appd binary
diff --git a/docs/release-notes/pending-release.md b/docs/release-notes/pending-release.md
index c7e9198b..c3e0cf9d 100644
--- a/docs/release-notes/pending-release.md
+++ b/docs/release-notes/pending-release.md
@@ -4,4 +4,6 @@
 
 ### Node Operators (v4.0.0)
 
+- Node operators must upgrade to the latest release of celestia-app (e.g. v4.x.x) prior to signalling for the v4 upgrade. This release will run the latest v3.x.x celestia-app via the multiplexer prior to the v4 activation height. The latest v3.x.x release contains a reduction to the upgrade delay height on testnets. See <https://github.com/celestiaorg/celestia-app/pull/4414>.
+
 ### Library Consumers (v4.0.0)
diff --git a/go.mod b/go.mod
index 6ceaca95..24f26c0e 100644
--- a/go.mod
+++ b/go.mod
@@ -7,7 +7,7 @@ require (
 	cosmossdk.io/math v1.4.0
 	github.com/celestiaorg/blobstream-contracts/v3 v3.1.0
 	github.com/celestiaorg/go-square v1.1.1
-	github.com/celestiaorg/go-square/v2 v2.1.0
+	github.com/celestiaorg/go-square/v2 v2.2.0
 	github.com/celestiaorg/knuu v0.16.2
 	github.com/celestiaorg/nmt v0.23.0
 	github.com/celestiaorg/rsmt2d v0.14.0
@@ -34,7 +34,7 @@ require (
 	golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8
 	google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1
 	google.golang.org/grpc v1.68.0
-	google.golang.org/protobuf v1.36.4
+	google.golang.org/protobuf v1.36.6
 	gopkg.in/yaml.v2 v2.4.0
 	k8s.io/apimachinery v0.31.1
 )
diff --git a/go.sum b/go.sum
index 16963aa3..9ad81c92 100644
--- a/go.sum
+++ b/go.sum
@@ -324,8 +324,8 @@ github.com/celestiaorg/cosmos-sdk v1.27.0-sdk-v0.46.16 h1:qxWiGrDEcg4FzVTpIXU/v3
 github.com/celestiaorg/cosmos-sdk v1.27.0-sdk-v0.46.16/go.mod h1:W30mNt3+2l516HVR8Gt9+Gf4qOrWC9/x18MTEx2GljE=
 github.com/celestiaorg/go-square v1.1.1 h1:Cy3p8WVspVcyOqHM8BWFuuYPwMitO1pYGe+ImILFZRA=
 github.com/celestiaorg/go-square v1.1.1/go.mod h1:1EXMErhDrWJM8B8V9hN7dqJ2kUTClfwdqMOmF9yQUa0=
-github.com/celestiaorg/go-square/v2 v2.1.0 h1:ECIvYEeHIWiIJGDCJxQNtzqm5DmnBly7XGhSpLsl+Lw=
-github.com/celestiaorg/go-square/v2 v2.1.0/go.mod h1:n3ztrh8CBjWOD6iWYMo3pPOlQIgzLK9yrnqMPcNo6g8=
+github.com/celestiaorg/go-square/v2 v2.2.0 h1:zJnUxCYc65S8FgUfVpyG/osDcsnjzo/JSXw/Uwn8zp4=
+github.com/celestiaorg/go-square/v2 v2.2.0/go.mod h1:j8kQUqJLYtcvCQMQV6QjEhUdaF7rBTXF74g8LbkR0Co=
 github.com/celestiaorg/ibc-go/v6 v6.2.4 h1:S9jH7e+faYWF9PgDQAQnHxgm+FeDWTKcOF99Zez40bA=
 github.com/celestiaorg/ibc-go/v6 v6.2.4/go.mod h1:XLsARy4Y7+GtAqzMcxNdlQf6lx+ti1e8KcMGv5NIK7A=
 github.com/celestiaorg/knuu v0.16.2 h1:OOl2xMf+5ryWtPcSF+M6gmw4YQPEh6lsCA/lTDl8fwI=
@@ -2019,8 +2019,8 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ
 google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
 google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
 google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
-google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM=
-google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
+google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
+google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
 gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
diff --git a/pkg/appconsts/chain_ids.go b/pkg/appconsts/chain_ids.go
index 5a7e7a0e..ed0de976 100644
--- a/pkg/appconsts/chain_ids.go
+++ b/pkg/appconsts/chain_ids.go
@@ -4,6 +4,8 @@ const (
 	ArabicaChainID = "arabica-11"
 	MochaChainID   = "mocha-4"
 	MainnetChainID = "celestia"
+	// TestChainID is the chain ID used for testing.
+	TestChainID = "test"
 )
 
 var PublicNetworks = []string{ArabicaChainID, MochaChainID, MainnetChainID}
diff --git a/pkg/appconsts/overrides.go b/pkg/appconsts/overrides.go
index 39c5f4c6..2404b320 100644
--- a/pkg/appconsts/overrides.go
+++ b/pkg/appconsts/overrides.go
@@ -6,5 +6,4 @@ package appconsts
 // Look at the Makefile to see how these are set.
 var (
 	OverrideSquareSizeUpperBoundStr string
-	OverrideUpgradeHeightDelayStr   string
 )
diff --git a/pkg/appconsts/versioned_consts.go b/pkg/appconsts/versioned_consts.go
index 60344535..621bc830 100644
--- a/pkg/appconsts/versioned_consts.go
+++ b/pkg/appconsts/versioned_consts.go
@@ -11,6 +11,9 @@ import (
 
 const (
 	LatestVersion = v3.Version
+	// numBlocksPerDay is the number of blocks in a day assuming a block
+	// interval of 6 seconds. Equivalent to 14,400 blocks.
+	numBlocksPerDay = int64(24 * 60 * 60 / 6)
 )
 
 // SubtreeRootThreshold works as a target upper bound for the number of subtree
@@ -78,16 +81,15 @@ func GetTimeoutCommit(v uint64) time.Duration {
 	}
 }
 
-// UpgradeHeightDelay returns the delay in blocks after a quorum has been reached that the chain should upgrade to the new version.
-func UpgradeHeightDelay(chainID string, v uint64) int64 {
-	if OverrideUpgradeHeightDelayStr != "" {
-		parsedValue, err := strconv.ParseInt(OverrideUpgradeHeightDelayStr, 10, 64)
-		if err != nil {
-			panic("Invalid OverrideUpgradeHeightDelayStr value")
-		}
-		return parsedValue
+// UpgradeHeightDelay returns the delay in blocks after a quorum has been
+// reached that the chain should upgrade to the new version. The version
+// argument should be the current application version, not the version after the
+// upgrade.
+func UpgradeHeightDelay(chainID string, version uint64) int64 {
+	if chainID == TestChainID {
+		return 3
 	}
-	switch v {
+	switch version {
 	case v1.Version:
 		return v1.UpgradeHeightDelay
 	case v2.Version:
@@ -98,8 +100,16 @@ func UpgradeHeightDelay(chainID string, v uint64) int64 {
 			return v3.UpgradeHeightDelay
 		}
 		return v2.UpgradeHeightDelay
-	default:
-		return v3.UpgradeHeightDelay
-
+	case v3.Version:
+		switch chainID {
+		case ArabicaChainID:
+			return numBlocksPerDay
+		case MochaChainID:
+			return numBlocksPerDay * 2
+		case MainnetChainID:
+			return v3.UpgradeHeightDelay
+		}
 	}
+	// TODO: this should panic because this should never be invoked for v4+
+	return v3.UpgradeHeightDelay
 }
diff --git a/x/blob/ante/ante.go b/x/blob/ante/ante.go
index 3c5249b2..1c9989f8 100644
--- a/x/blob/ante/ante.go
+++ b/x/blob/ante/ante.go
@@ -8,6 +8,7 @@ import (
 	"cosmossdk.io/errors"
 	sdk "github.com/cosmos/cosmos-sdk/types"
 	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
+	"github.com/cosmos/cosmos-sdk/x/authz"
 )
 
 // 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
 		return next(ctx, tx, simulate)
 	}
 
-	var gasPerByte uint32
+	// Skip gas checks during genesis initialization
+	if ctx.BlockHeight() == 0 {
+		return next(ctx, tx, simulate)
+	}
+
+	gasPerByte := d.getGasPerByte(ctx)
 	txGas := ctx.GasMeter().GasRemaining()
-	for _, m := range tx.GetMsgs() {
-		// NOTE: here we assume only one PFB per transaction
-		if pfb, ok := m.(*types.MsgPayForBlobs); ok {
-			if gasPerByte == 0 {
-				if ctx.BlockHeader().Version.App <= v2.Version {
-					// lazily fetch the gas per byte param
-					gasPerByte = d.k.GasPerBlobByte(ctx)
-				} else {
-					gasPerByte = appconsts.GasPerBlobByte(ctx.BlockHeader().Version.App)
-				}
+	err := d.validatePFBHasEnoughGas(tx.GetMsgs(), gasPerByte, txGas)
+	if err != nil {
+		return ctx, err
+	}
+
+	return next(ctx, tx, simulate)
+}
+
+// validatePFBHasEnoughGas iterates through all the msgs and nested msgs to find
+// a MsgPayForBlobs. If found, it validates that the txGas is enough to pay for
+// the blobs.
+func (d MinGasPFBDecorator) validatePFBHasEnoughGas(msgs []sdk.Msg, gasPerByte uint32, txGas uint64) error {
+	for _, m := range msgs {
+		if execMsg, ok := m.(*authz.MsgExec); ok {
+			// Recursively look for PFBs in nested authz messages.
+			nestedMsgs, err := execMsg.GetMessages()
+			if err != nil {
+				return err
 			}
-			gasToConsume := pfb.Gas(gasPerByte)
-			if gasToConsume > txGas {
-				return ctx, errors.Wrapf(sdkerrors.ErrInsufficientFee, "not enough gas to pay for blobs (minimum: %d, got: %d)", gasToConsume, txGas)
+			err = d.validatePFBHasEnoughGas(nestedMsgs, gasPerByte, txGas)
+			if err != nil {
+				return err
+			}
+		}
+		if pfb, ok := m.(*types.MsgPayForBlobs); ok {
+			err := validateEnoughGas(pfb, gasPerByte, txGas)
+			if err != nil {
+				return err
 			}
 		}
 	}
+	return nil
+}
 
-	return next(ctx, tx, simulate)
+func (d MinGasPFBDecorator) getGasPerByte(ctx sdk.Context) uint32 {
+	if ctx.BlockHeader().Version.App <= v2.Version {
+		return d.k.GasPerBlobByte(ctx)
+	}
+	return appconsts.GasPerBlobByte(ctx.BlockHeader().Version.App)
+}
+
+// validateEnoughGas returns an error if the gas needed to pay for the blobs is
+// greater than the txGas.
+func validateEnoughGas(msg *types.MsgPayForBlobs, gasPerByte uint32, txGas uint64) error {
+	gasToConsume := msg.Gas(gasPerByte)
+	if gasToConsume > txGas {
+		return errors.Wrapf(sdkerrors.ErrInsufficientFee, "not enough gas to pay for blobs (minimum: %d, got: %d)", gasToConsume, txGas)
+	}
+	return nil
 }
 
 type BlobKeeper interface {
diff --git a/x/blob/ante/blob_share_decorator.go b/x/blob/ante/blob_share_decorator.go
index 90eb7780..e863cfce 100644
--- a/x/blob/ante/blob_share_decorator.go
+++ b/x/blob/ante/blob_share_decorator.go
@@ -1,13 +1,15 @@
 package ante
 
 import (
+	"math"
+
+	"cosmossdk.io/errors"
 	"github.com/celestiaorg/celestia-app/v3/pkg/appconsts"
 	v1 "github.com/celestiaorg/celestia-app/v3/pkg/appconsts/v1"
 	blobtypes "github.com/celestiaorg/celestia-app/v3/x/blob/types"
 	"github.com/celestiaorg/go-square/v2/share"
-
-	"cosmossdk.io/errors"
 	sdk "github.com/cosmos/cosmos-sdk/types"
+	"github.com/cosmos/cosmos-sdk/x/authz"
 )
 
 // 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
 		return next(ctx, tx, simulate)
 	}
 
+	txBytes := ctx.TxBytes()
+	if len(txBytes) > math.MaxUint32 {
+		return ctx, errors.Wrapf(blobtypes.ErrBlobsTooLarge, "the tx size %d exceeds the max uint32", txBytes)
+	}
+	txSize := uint32(len(txBytes))
 	maxBlobShares := d.getMaxBlobShares(ctx)
-	for _, m := range tx.GetMsgs() {
+
+	err := d.validateMsgs(tx.GetMsgs(), txSize, maxBlobShares)
+	if err != nil {
+		return ctx, err
+	}
+
+	return next(ctx, tx, simulate)
+}
+
+func (d BlobShareDecorator) validateMsgs(msgs []sdk.Msg, txSize uint32, maxBlobShares int) error {
+	for _, m := range msgs {
+		if execMsg, ok := m.(*authz.MsgExec); ok {
+			// Recursively look for PFBs in nested authz messages.
+			nestedMsgs, err := execMsg.GetMessages()
+			if err != nil {
+				return err
+			}
+			err = d.validateMsgs(nestedMsgs, txSize, maxBlobShares)
+			if err != nil {
+				return err
+			}
+		}
+
 		if pfb, ok := m.(*blobtypes.MsgPayForBlobs); ok {
-			if sharesNeeded := getSharesNeeded(uint32(len(ctx.TxBytes())), pfb.BlobSizes); sharesNeeded > maxBlobShares {
-				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)
+			if sharesNeeded := getSharesNeeded(txSize, pfb.BlobSizes); sharesNeeded > maxBlobShares {
+				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)
 			}
 		}
 	}
-
-	return next(ctx, tx, simulate)
+	return nil
 }
 
 // getMaxBlobShares returns the max the number of shares available for blob data.



Test patch:
diff --git a/app/test/upgrade_test.go b/app/test/upgrade_test.go
index e5432207..81b1d6ab 100644
--- a/app/test/upgrade_test.go
+++ b/app/test/upgrade_test.go
@@ -38,9 +38,6 @@ func TestAppUpgradeV3(t *testing.T) {
 		t.Skip("skipping TestAppUpgradeV3 in short mode")
 	}
 
-	appconsts.OverrideUpgradeHeightDelayStr = "1"
-	defer func() { appconsts.OverrideUpgradeHeightDelayStr = "" }()
-
 	testApp, genesis := SetupTestAppWithUpgradeHeight(t, 3)
 	upgradeFromV1ToV2(t, testApp)
 
@@ -252,13 +249,14 @@ func TestBlobstreamRemovedInV2(t *testing.T) {
 	require.Error(t, err)
 }
 
-func SetupTestAppWithUpgradeHeight(t *testing.T, upgradeHeight int64) (*app.App, *genesis.Genesis) {
+func SetupTestAppWithUpgradeHeight(t *testing.T, upgradeHeightV2 int64) (*app.App, *genesis.Genesis) {
 	t.Helper()
 
 	db := dbm.NewMemDB()
 	encCfg := encoding.MakeConfig(app.ModuleEncodingRegisters...)
-	testApp := app.New(log.NewNopLogger(), db, nil, 0, encCfg, upgradeHeight, 0, util.EmptyAppOptions{})
+	testApp := app.New(log.NewNopLogger(), db, nil, 0, encCfg, upgradeHeightV2, 0, util.EmptyAppOptions{})
 	genesis := genesis.NewDefaultGenesis().
+		WithChainID(appconsts.TestChainID).
 		WithValidators(genesis.NewDefaultValidator(testnode.DefaultValidatorAccountName)).
 		WithConsensusParams(app.DefaultInitialConsensusParams())
 	genDoc, err := genesis.Export()
diff --git a/pkg/appconsts/versioned_consts_test.go b/pkg/appconsts/versioned_consts_test.go
index 249dd998..01d453b4 100644
--- a/pkg/appconsts/versioned_consts_test.go
+++ b/pkg/appconsts/versioned_consts_test.go
@@ -113,10 +113,40 @@ func TestUpgradeHeightDelay(t *testing.T) {
 			expectedUpgradeHeightDelay: v3.UpgradeHeightDelay, // falls back to v3 because of arabica bug
 		},
 		{
-			name:                       "v3 upgrade delay",
-			chainID:                    "mocha-4",
-			version:                    3,
-			expectedUpgradeHeightDelay: v3.UpgradeHeightDelay,
+			name:                       "the upgrade delay for chainID 'test' should be 3 regardless of the version",
+			chainID:                    appconsts.TestChainID,
+			version:                    v3.Version,
+			expectedUpgradeHeightDelay: 3,
+		},
+		{
+			name:                       "the upgrade delay for chainID 'test' should be 3 regardless of the version",
+			chainID:                    appconsts.TestChainID,
+			version:                    4,
+			expectedUpgradeHeightDelay: 3,
+		},
+		{
+			name:                       "v3 upgrade delay on arabica is 1 day",
+			chainID:                    appconsts.ArabicaChainID,
+			version:                    v3.Version,
+			expectedUpgradeHeightDelay: 14_400, // 1 day of 6 second blocks
+		},
+		{
+			name:                       "v3 upgrade delay on mocha is 2 days",
+			chainID:                    appconsts.MochaChainID,
+			version:                    v3.Version,
+			expectedUpgradeHeightDelay: 28_800, // 2 days of 6 second blocks
+		},
+		{
+			name:                       "v3 upgrade delay on mainnet is 7 days",
+			chainID:                    appconsts.MainnetChainID,
+			version:                    v3.Version,
+			expectedUpgradeHeightDelay: v3.UpgradeHeightDelay, // 7 days of 6 second blocks
+		},
+		{
+			name:                       "v4 upgrade delay on arabica is 7 days",
+			chainID:                    appconsts.ArabicaChainID,
+			version:                    4,
+			expectedUpgradeHeightDelay: v3.UpgradeHeightDelay, // TODO: this should panic
 		},
 	}
 
diff --git a/x/blob/ante/ante_test.go b/x/blob/ante/ante_test.go
index 6be76d77..6e520973 100644
--- a/x/blob/ante/ante_test.go
+++ b/x/blob/ante/ante_test.go
@@ -2,6 +2,7 @@ package ante_test
 
 import (
 	"fmt"
+	"math"
 	"testing"
 
 	"github.com/celestiaorg/celestia-app/v3/app"
@@ -12,6 +13,8 @@ import (
 	blob "github.com/celestiaorg/celestia-app/v3/x/blob/types"
 	"github.com/celestiaorg/go-square/v2/share"
 	sdk "github.com/cosmos/cosmos-sdk/types"
+	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
+	"github.com/cosmos/cosmos-sdk/x/authz"
 	"github.com/stretchr/testify/require"
 	tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
 	"github.com/tendermint/tendermint/proto/tendermint/version"
@@ -124,6 +127,7 @@ func TestPFBAnteHandler(t *testing.T) {
 					Version: version.Consensus{
 						App: currentVersion,
 					},
+					Height: 1,
 				}, true, nil).WithGasMeter(sdk.NewGasMeter(uint64(tc.txGas(gasPerBlobByte)))).WithIsCheckTx(true)
 
 				ctx.GasMeter().ConsumeGas(tc.gasConsumed, "test")
@@ -141,6 +145,49 @@ func TestPFBAnteHandler(t *testing.T) {
 	}
 }
 
+// TestMinGasPFBDecoratorWithMsgExec tests that the MinGasPFBDecorator rejects a
+// MsgExec that contains a MsgExec with a MsgPayForBlob where the MsgPayForBlob
+// gas cost is greater than the tx's gas limit.
+func TestMinGasPFBDecoratorWithMsgExec(t *testing.T) {
+	anteHandler := ante.NewMinGasPFBDecorator(mockBlobKeeper{})
+	txConfig := encoding.MakeConfig(app.ModuleEncodingRegisters...).TxConfig
+
+	// Create a context with a gas meter with a high gas limit
+	gasLimit := uint64(100_000_000)
+	ctx := sdk.NewContext(nil, tmproto.Header{
+		Version: version.Consensus{
+			App: appconsts.LatestVersion,
+		},
+		Height: 1,
+	}, true, nil).WithGasMeter(sdk.NewGasMeter(gasLimit)).WithIsCheckTx(true)
+
+	// Build a tx with a MsgExec containing a MsgPayForBlobs with a huge gas cost
+	txBuilder := txConfig.NewTxBuilder()
+	msgExec := authz.NewMsgExec(sdk.AccAddress{}, []sdk.Msg{
+		&blob.MsgPayForBlobs{
+			Signer:    "celestia...",
+			BlobSizes: []uint32{uint32(math.MaxUint32)},
+		},
+	})
+	require.NoError(t, txBuilder.SetMsgs(&msgExec))
+	tx := txBuilder.GetTx()
+
+	// Run the ante handler
+	_, err := anteHandler.AnteHandle(ctx, tx, false, mockNext)
+	require.Error(t, err)
+	require.ErrorIs(t, err, sdkerrors.ErrInsufficientFee)
+
+	// Create a MsgExec that wraps a MsgExec that contains a MsgPayForBlobs with a huge gas cost
+	nestedMsgExec := authz.NewMsgExec(sdk.AccAddress{}, []sdk.Msg{&msgExec})
+	require.NoError(t, txBuilder.SetMsgs(&nestedMsgExec))
+	tx = txBuilder.GetTx()
+
+	// Run the ante handler
+	_, err = anteHandler.AnteHandle(ctx, tx, false, mockNext)
+	require.Error(t, err)
+	require.ErrorIs(t, err, sdkerrors.ErrInsufficientFee)
+}
+
 type mockBlobKeeper struct{}
 
 func (mockBlobKeeper) GasPerBlobByte(_ sdk.Context) uint32 {
diff --git a/x/blob/ante/blob_share_decorator_test.go b/x/blob/ante/blob_share_decorator_test.go
index e65dca6c..736e851e 100644
--- a/x/blob/ante/blob_share_decorator_test.go
+++ b/x/blob/ante/blob_share_decorator_test.go
@@ -1,10 +1,12 @@
 package ante_test
 
 import (
+	"math"
 	"testing"
 
 	"github.com/celestiaorg/celestia-app/v3/app"
 	"github.com/celestiaorg/celestia-app/v3/app/encoding"
+	"github.com/celestiaorg/celestia-app/v3/pkg/appconsts"
 	v1 "github.com/celestiaorg/celestia-app/v3/pkg/appconsts/v1"
 	v2 "github.com/celestiaorg/celestia-app/v3/pkg/appconsts/v2"
 	"github.com/celestiaorg/celestia-app/v3/pkg/user"
@@ -16,6 +18,7 @@ import (
 	"github.com/celestiaorg/go-square/v2/share"
 	blobtx "github.com/celestiaorg/go-square/v2/tx"
 	sdk "github.com/cosmos/cosmos-sdk/types"
+	"github.com/cosmos/cosmos-sdk/x/authz"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 	tmrand "github.com/tendermint/tendermint/libs/rand"
@@ -166,6 +169,39 @@ func TestBlobShareDecorator(t *testing.T) {
 	}
 }
 
+func TestBlobShareDecoratorWithMsgExec(t *testing.T) {
+	decorator := ante.NewBlobShareDecorator(mockBlobKeeper{})
+	ctx := sdk.Context{}.
+		WithIsCheckTx(true).
+		WithBlockHeader(tmproto.Header{Version: version.Consensus{App: appconsts.LatestVersion}})
+
+	txConfig := encoding.MakeConfig(app.ModuleEncodingRegisters...).TxConfig
+	txBuilder := txConfig.NewTxBuilder()
+	// Create a tx with a MsgExec that contains a MsgPayForBlobs with a huge
+	// blob that can not fit in a square.
+	msgExec := authz.NewMsgExec(sdk.AccAddress{}, []sdk.Msg{
+		&blob.MsgPayForBlobs{
+			Signer:    "celestia...",
+			BlobSizes: []uint32{uint32(math.MaxUint32)},
+		},
+	})
+	require.NoError(t, txBuilder.SetMsgs(&msgExec))
+	tx := txBuilder.GetTx()
+
+	_, err := decorator.AnteHandle(ctx, tx, false, mockNext)
+	assert.Error(t, err)
+	assert.ErrorIs(t, err, blob.ErrBlobsTooLarge)
+
+	// Create a MsgExec that wraps a MsgExec that contains a MsgPayForBlobs with a huge
+	// blob that can not fit in a square.
+	nestedMsgExec := authz.NewMsgExec(sdk.AccAddress{}, []sdk.Msg{&msgExec})
+	require.NoError(t, txBuilder.SetMsgs(&nestedMsgExec))
+	tx = txBuilder.GetTx()
+	_, err = decorator.AnteHandle(ctx, tx, false, mockNext)
+	assert.Error(t, err)
+	assert.ErrorIs(t, err, blob.ErrBlobsTooLarge)
+}
+
 func mockNext(ctx sdk.Context, _ sdk.Tx, _ bool) (sdk.Context, error) {
 	return ctx, nil
 }
diff --git a/x/signal/keeper_test.go b/x/signal/keeper_test.go
index 676c93f8..cc2f84a6 100644
--- a/x/signal/keeper_test.go
+++ b/x/signal/keeper_test.go
@@ -426,7 +426,7 @@ func TestGetUpgrade(t *testing.T) {
 		got, err := upgradeKeeper.GetUpgrade(ctx, &types.QueryGetUpgradeRequest{})
 		require.NoError(t, err)
 		assert.Equal(t, v2.Version, got.Upgrade.AppVersion)
-		assert.Equal(t, appconsts.UpgradeHeightDelay("test", v2.Version), got.Upgrade.UpgradeHeight)
+		assert.Equal(t, appconsts.UpgradeHeightDelay(appconsts.TestChainID, v1.Version), got.Upgrade.UpgradeHeight)
 	})
 }