diff --git a/app/max_blob_size.go b/app/max_blob_size.go new file mode 100644 index 0000000000..0208a8ecc7 --- /dev/null +++ b/app/max_blob_size.go @@ -0,0 +1,29 @@ +package app + +import ( + "github.com/celestiaorg/celestia-app/pkg/appconsts" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// MaxBlobSize returns an upper bound for the maximum blob size that can fit in +// a single data square. Since the returned value is an upper bound, it is +// possible that slightly smaller blob may not fit due to shares that aren't +// occupied by the blob (i.e. the PFB tx shares). +func (app *App) MaxBlobSize(ctx sdk.Context) int { + maxSquareSize := app.GovSquareSizeUpperBound(ctx) + maxShares := maxSquareSize * maxSquareSize + maxShareBytes := maxShares * appconsts.ContinuationSparseShareContentSize + + // TODO(rootulp): get MaxBytes consensus params from core + maxBlockBytes := appconsts.DefaultMaxBytes + + return min(maxShareBytes, maxBlockBytes) +} + +// min returns the smaller of a and b. +func min(a, b int) int { + if a < b { + return a + } + return b +} diff --git a/app/test/max_blob_size_test.go b/app/test/max_blob_size_test.go new file mode 100644 index 0000000000..7f7b120c9d --- /dev/null +++ b/app/test/max_blob_size_test.go @@ -0,0 +1,55 @@ +package app_test + +import ( + "testing" + + "github.com/celestiaorg/celestia-app/app" + "github.com/celestiaorg/celestia-app/app/encoding" + testutil "github.com/celestiaorg/celestia-app/test/util" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" + tmrand "github.com/tendermint/tendermint/libs/rand" + core "github.com/tendermint/tendermint/proto/tendermint/types" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + coretypes "github.com/tendermint/tendermint/types" +) + +// TestMaxBlobSize verifies that a blob size of MaxBlobSize can not fit in a +// block. +func TestMaxBlobSize(t *testing.T) { + if testing.Short() { + t.Skip("skipping TestMaxBlobSize in short mode.") + } + encConf := encoding.MakeConfig(app.ModuleEncodingRegisters...) + accounts := make([]string, 1) + for i := range accounts { + accounts[i] = tmrand.Str(20) + } + + cparams := app.DefaultConsensusParams() + testApp, kr := testutil.SetupTestAppWithGenesisValSet(cparams, accounts...) + ctx := sdk.NewContext(testApp.CommitMultiStore(), tmproto.Header{}, false, nil) + + maxBlobSize := testApp.MaxBlobSize(ctx) + txs := testutil.RandBlobTxsWithAccounts( + t, + testApp, + encConf.TxConfig.TxEncoder(), + kr, + maxBlobSize, + 1, + false, + testutil.ChainID, + accounts, + ) + resp := testApp.PrepareProposal(abci.RequestPrepareProposal{ + BlockData: &core.Data{ + Txs: coretypes.Txs(txs).ToSliceOfBytes(), + }, + ChainId: testutil.ChainID, + }) + + // Verify that the blob tx wasn't included in the block. + require.Empty(t, 0, resp.BlockData.Txs) +}