diff --git a/app/ante/ante.go b/app/ante/ante.go index dd65fdd120..89b6c1d050 100644 --- a/app/ante/ante.go +++ b/app/ante/ante.go @@ -42,7 +42,7 @@ func NewAnteHandler( ante.NewValidateMemoDecorator(accountKeeper), // Ensure the tx's gas limit is > the gas consumed based on the tx size. // Side effect: consumes gas from the gas meter. - ante.NewConsumeGasForTxSizeDecorator(accountKeeper), + NewConsumeGasForTxSizeDecorator(accountKeeper), // Ensure the feepayer (fee granter or first signer) has enough funds to pay for the tx. // Ensure the gas price >= network min gas price if app version >= 2. // Side effect: deducts fees from the fee payer. Sets the tx priority in context. diff --git a/app/ante/min_fee_test.go b/app/ante/min_fee_test.go index 30912813c4..fc66e11a67 100644 --- a/app/ante/min_fee_test.go +++ b/app/ante/min_fee_test.go @@ -9,7 +9,6 @@ import ( "github.com/celestiaorg/celestia-app/v3/app/ante" "github.com/celestiaorg/celestia-app/v3/app/encoding" "github.com/celestiaorg/celestia-app/v3/pkg/appconsts" - v2 "github.com/celestiaorg/celestia-app/v3/pkg/appconsts/v2" "github.com/celestiaorg/celestia-app/v3/test/util/testnode" "github.com/celestiaorg/celestia-app/v3/x/minfee" "github.com/cosmos/cosmos-sdk/codec" @@ -58,7 +57,7 @@ func TestValidateTxFee(t *testing.T) { { name: "bad tx; fee below required minimum", fee: sdk.NewCoins(sdk.NewInt64Coin(appconsts.BondDenom, feeAmount-1)), - gasLimit: uint64(float64(feeAmount) / v2.NetworkMinGasPrice), + gasLimit: uint64(float64(feeAmount) / appconsts.DefaultNetworkMinGasPrice), appVersion: uint64(2), isCheckTx: false, expErr: true, @@ -66,7 +65,7 @@ func TestValidateTxFee(t *testing.T) { { name: "good tx; fee equal to required minimum", fee: sdk.NewCoins(sdk.NewInt64Coin(appconsts.BondDenom, feeAmount)), - gasLimit: uint64(float64(feeAmount) / v2.NetworkMinGasPrice), + gasLimit: uint64(float64(feeAmount) / appconsts.DefaultNetworkMinGasPrice), appVersion: uint64(2), isCheckTx: false, expErr: false, @@ -74,7 +73,7 @@ func TestValidateTxFee(t *testing.T) { { name: "good tx; fee above required minimum", fee: sdk.NewCoins(sdk.NewInt64Coin(appconsts.BondDenom, feeAmount+1)), - gasLimit: uint64(float64(feeAmount) / v2.NetworkMinGasPrice), + gasLimit: uint64(float64(feeAmount) / appconsts.DefaultNetworkMinGasPrice), appVersion: uint64(2), isCheckTx: false, expErr: false, @@ -82,7 +81,7 @@ func TestValidateTxFee(t *testing.T) { { name: "good tx; with no fee (v1)", fee: sdk.NewCoins(sdk.NewInt64Coin(appconsts.BondDenom, feeAmount)), - gasLimit: uint64(float64(feeAmount) / v2.NetworkMinGasPrice), + gasLimit: uint64(float64(feeAmount) / appconsts.DefaultNetworkMinGasPrice), appVersion: uint64(1), isCheckTx: false, expErr: false, @@ -143,7 +142,7 @@ func TestValidateTxFee(t *testing.T) { ctx = ctx.WithMinGasPrices(sdk.DecCoins{validatorMinGasPriceCoin}) - networkMinGasPriceDec, err := sdk.NewDecFromStr(fmt.Sprintf("%f", v2.NetworkMinGasPrice)) + networkMinGasPriceDec, err := sdk.NewDecFromStr(fmt.Sprintf("%f", appconsts.DefaultNetworkMinGasPrice)) require.NoError(t, err) subspace, _ := paramsKeeper.GetSubspace(minfee.ModuleName) diff --git a/app/ante/tx_size.go b/app/ante/tx_size.go new file mode 100644 index 0000000000..356f6f2646 --- /dev/null +++ b/app/ante/tx_size.go @@ -0,0 +1,151 @@ +package ante + +import ( + "encoding/hex" + + "cosmossdk.io/errors" + "github.com/celestiaorg/celestia-app/v3/pkg/appconsts" + v2 "github.com/celestiaorg/celestia-app/v3/pkg/appconsts/v2" + "github.com/cosmos/cosmos-sdk/codec/legacy" + "github.com/cosmos/cosmos-sdk/crypto/keys/multisig" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/types/tx/signing" + "github.com/cosmos/cosmos-sdk/x/auth/ante" + "github.com/cosmos/cosmos-sdk/x/auth/migrations/legacytx" + authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" + auth "github.com/cosmos/cosmos-sdk/x/auth/types" +) + +var ( + // Simulation signature values used to estimate gas consumption. + key = make([]byte, secp256k1.PubKeySize) + simSecp256k1Pubkey = &secp256k1.PubKey{Key: key} + simSecp256k1Sig [64]byte +) + +func init() { + // Decodes a valid hex string into a sepc256k1Pubkey for use in transaction simulation + bz, _ := hex.DecodeString("035AD6810A47F073553FF30D2FCC7E0D3B1C0B74B61A1AAA2582344037151E143A") + copy(key, bz) + simSecp256k1Pubkey.Key = key +} + +// ConsumeTxSizeGasDecorator will take in parameters and consume gas proportional +// to the size of tx before calling next AnteHandler. Note, the gas costs will be +// slightly over estimated due to the fact that any given signing account may need +// to be retrieved from state. +// +// CONTRACT: If simulate=true, then signatures must either be completely filled +// in or empty. +// CONTRACT: To use this decorator, signatures of transaction must be represented +// as legacytx.StdSignature otherwise simulate mode will incorrectly estimate gas cost. + +// The code was copied from celestia's fork of the cosmos-sdk: +// https://github.com/celestiaorg/cosmos-sdk/blob/release/v0.46.x-celestia/x/auth/ante/basic.go +// In app versions v2 and below, the txSizeCostPerByte used for gas cost estimation is taken from the auth module. +// In app v3 and above, the versioned constant appconsts.TxSizeCostPerByte is used. +type ConsumeTxSizeGasDecorator struct { + ak ante.AccountKeeper +} + +func NewConsumeGasForTxSizeDecorator(ak ante.AccountKeeper) ConsumeTxSizeGasDecorator { + return ConsumeTxSizeGasDecorator{ + ak: ak, + } +} + +func (cgts ConsumeTxSizeGasDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) { + sigTx, ok := tx.(authsigning.SigVerifiableTx) + if !ok { + return ctx, errors.Wrap(sdkerrors.ErrTxDecode, "invalid tx type") + } + params := cgts.ak.GetParams(ctx) + + consumeGasForTxSize(ctx, sdk.Gas(len(ctx.TxBytes())), params) + + // simulate gas cost for signatures in simulate mode + if simulate { + // in simulate mode, each element should be a nil signature + sigs, err := sigTx.GetSignaturesV2() + if err != nil { + return ctx, err + } + n := len(sigs) + + for i, signer := range sigTx.GetSigners() { + // if signature is already filled in, no need to simulate gas cost + if i < n && !isIncompleteSignature(sigs[i].Data) { + continue + } + + var pubkey cryptotypes.PubKey + + acc := cgts.ak.GetAccount(ctx, signer) + + // use placeholder simSecp256k1Pubkey if sig is nil + if acc == nil || acc.GetPubKey() == nil { + pubkey = simSecp256k1Pubkey + } else { + pubkey = acc.GetPubKey() + } + + // use stdsignature to mock the size of a full signature + simSig := legacytx.StdSignature{ //nolint:staticcheck // this will be removed when proto is ready + Signature: simSecp256k1Sig[:], + PubKey: pubkey, + } + + sigBz := legacy.Cdc.MustMarshal(simSig) + txBytes := sdk.Gas(len(sigBz) + 6) + + // If the pubkey is a multi-signature pubkey, then we estimate for the maximum + // number of signers. + if _, ok := pubkey.(*multisig.LegacyAminoPubKey); ok { + txBytes *= params.TxSigLimit + } + + consumeGasForTxSize(ctx, txBytes, params) + } + } + + return next(ctx, tx, simulate) +} + +// isIncompleteSignature tests whether SignatureData is fully filled in for simulation purposes +func isIncompleteSignature(data signing.SignatureData) bool { + if data == nil { + return true + } + + switch data := data.(type) { + case *signing.SingleSignatureData: + return len(data.Signature) == 0 + case *signing.MultiSignatureData: + if len(data.Signatures) == 0 { + return true + } + for _, s := range data.Signatures { + if isIncompleteSignature(s) { + return true + } + } + } + + return false +} + +// consumeGasForTxSize consumes gas based on the size of the transaction. +// It uses different parameters depending on the app version. +func consumeGasForTxSize(ctx sdk.Context, txBytes uint64, params auth.Params) { + // For app v2 and below we should get txSizeCostPerByte from auth module + if ctx.BlockHeader().Version.App <= v2.Version { + ctx.GasMeter().ConsumeGas(params.TxSizeCostPerByte*txBytes, "txSize") + } else { + // From v3 onwards, we should get txSizeCostPerByte from appconsts + txSizeCostPerByte := appconsts.TxSizeCostPerByte(ctx.BlockHeader().Version.App) + ctx.GasMeter().ConsumeGas(txSizeCostPerByte*txBytes, "txSize") + } +} diff --git a/app/ante/tx_size_test.go b/app/ante/tx_size_test.go new file mode 100644 index 0000000000..6b6bdbd80b --- /dev/null +++ b/app/ante/tx_size_test.go @@ -0,0 +1,197 @@ +package ante_test + +import ( + "fmt" + "strings" + "testing" + + "github.com/celestiaorg/celestia-app/v3/app" + "github.com/celestiaorg/celestia-app/v3/app/ante" + "github.com/celestiaorg/celestia-app/v3/pkg/appconsts" + v2 "github.com/celestiaorg/celestia-app/v3/pkg/appconsts/v2" + testutil "github.com/celestiaorg/celestia-app/v3/test/util" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/tx" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/crypto/types/multisig" + "github.com/cosmos/cosmos-sdk/simapp" + "github.com/cosmos/cosmos-sdk/testutil/testdata" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/tx/signing" + xauthsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/stretchr/testify/require" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + "github.com/tendermint/tendermint/proto/tendermint/version" +) + +const TxSizeCostPerByte = 8 + +func setup() (*app.App, sdk.Context, client.Context, error) { + app, _, _ := testutil.NewTestAppWithGenesisSet(app.DefaultConsensusParams()) + ctx := app.NewContext(false, tmproto.Header{}) + params := authtypes.DefaultParams() + // Override default with a different TxSizeCostPerByte value for testing + params.TxSizeCostPerByte = TxSizeCostPerByte + app.AccountKeeper.SetParams(ctx, params) + ctx = ctx.WithBlockHeight(1) + + // Set up TxConfig. + encodingConfig := simapp.MakeTestEncodingConfig() + // We're using TestMsg encoding in the test, so register it here. + encodingConfig.Amino.RegisterConcrete(&testdata.TestMsg{}, "testdata.TestMsg", nil) + testdata.RegisterInterfaces(encodingConfig.InterfaceRegistry) + + clientCtx := client.Context{}. + WithTxConfig(encodingConfig.TxConfig) + + return app, ctx, clientCtx, nil +} + +func TestConsumeGasForTxSize(t *testing.T) { + app, ctx, clientCtx, err := setup() + require.NoError(t, err) + var txBuilder client.TxBuilder + + // keys and addresses + priv1, _, addr1 := testdata.KeyTestPubAddr() + + // msg and signatures + msg := testdata.NewTestMsg(addr1) + feeAmount := testdata.NewTestFeeAmount() + gasLimit := testdata.NewTestGasLimit() + + cgtsd := ante.NewConsumeGasForTxSizeDecorator(app.AccountKeeper) + antehandler := sdk.ChainAnteDecorators(cgtsd) + + testCases := []struct { + version uint64 + name string + sigV2 signing.SignatureV2 + }{ + {v2.Version, "SingleSignatureData v2", signing.SignatureV2{PubKey: priv1.PubKey()}}, + {v2.Version, "MultiSignatureData v2", signing.SignatureV2{PubKey: priv1.PubKey(), Data: multisig.NewMultisig(2)}}, + {appconsts.LatestVersion, fmt.Sprintf("SingleSignatureData v%d", appconsts.LatestVersion), signing.SignatureV2{PubKey: priv1.PubKey()}}, + {appconsts.LatestVersion, fmt.Sprintf("MultiSignatureData v%d", appconsts.LatestVersion), signing.SignatureV2{PubKey: priv1.PubKey(), Data: multisig.NewMultisig(2)}}, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + // set the version + ctx = app.NewContext(false, tmproto.Header{Version: version.Consensus{ + App: tc.version, + }}) + + txBuilder = clientCtx.TxConfig.NewTxBuilder() + require.NoError(t, txBuilder.SetMsgs(msg)) + txBuilder.SetFeeAmount(feeAmount) + txBuilder.SetGasLimit(gasLimit) + txBuilder.SetMemo(strings.Repeat("01234567890", 10)) + + privs, accNums, accSeqs := []cryptotypes.PrivKey{priv1}, []uint64{0}, []uint64{0} + tx, err := createTestTx(txBuilder, clientCtx, privs, accNums, accSeqs, ctx.ChainID()) + require.NoError(t, err) + + txBytes, err := clientCtx.TxConfig.TxJSONEncoder()(tx) + require.Nil(t, err, "Cannot marshal tx: %v", err) + + // expected TxSizeCostPerByte is different for each version + var txSizeCostPerByte uint64 + if tc.version == v2.Version { + txSizeCostPerByte = TxSizeCostPerByte + } else { + txSizeCostPerByte = appconsts.TxSizeCostPerByte(tc.version) + } + + expectedGas := sdk.Gas(len(txBytes)) * txSizeCostPerByte + + // set suite.ctx with TxBytes manually + ctx = ctx.WithTxBytes(txBytes) + + // track how much gas is necessary to retrieve parameters + beforeGas := ctx.GasMeter().GasConsumed() + app.AccountKeeper.GetParams(ctx) + afterGas := ctx.GasMeter().GasConsumed() + expectedGas += afterGas - beforeGas + + beforeGas = ctx.GasMeter().GasConsumed() + ctx, err = antehandler(ctx, tx, false) + require.Nil(t, err, "ConsumeTxSizeGasDecorator returned error: %v", err) + + // require that decorator consumes expected amount of gas + consumedGas := ctx.GasMeter().GasConsumed() - beforeGas + require.Equal(t, expectedGas, consumedGas, "Decorator did not consume the correct amount of gas") + + // simulation must not underestimate gas of this decorator even with nil signatures + txBuilder, err := clientCtx.TxConfig.WrapTxBuilder(tx) + require.NoError(t, err) + require.NoError(t, txBuilder.SetSignatures(tc.sigV2)) + tx = txBuilder.GetTx() + + simTxBytes, err := clientCtx.TxConfig.TxJSONEncoder()(tx) + require.Nil(t, err, "Cannot marshal tx: %v", err) + // require that simulated tx is smaller than tx with signatures + require.True(t, len(simTxBytes) < len(txBytes), "simulated tx still has signatures") + + // Set suite.ctx with smaller simulated TxBytes manually + ctx = ctx.WithTxBytes(simTxBytes) + + beforeSimGas := ctx.GasMeter().GasConsumed() + + // run antehandler with simulate=true + ctx, err = antehandler(ctx, tx, true) + consumedSimGas := ctx.GasMeter().GasConsumed() - beforeSimGas + + // require that antehandler passes and does not underestimate decorator cost + require.Nil(t, err, "ConsumeTxSizeGasDecorator returned error: %v", err) + require.True(t, consumedSimGas >= expectedGas, "Simulate mode underestimates gas on AnteDecorator. Simulated cost: %d, expected cost: %d", consumedSimGas, expectedGas) + }) + } +} + +// createTestTx creates a test tx given multiple inputs. +func createTestTx(txBuilder client.TxBuilder, clientCtx client.Context, privs []cryptotypes.PrivKey, accNums []uint64, accSeqs []uint64, chainID string) (xauthsigning.Tx, error) { + // First round: we gather all the signer infos. We use the "set empty + // signature" hack to do that. + sigsV2 := make([]signing.SignatureV2, 0, len(privs)) + for i, priv := range privs { + sigV2 := signing.SignatureV2{ + PubKey: priv.PubKey(), + Data: &signing.SingleSignatureData{ + SignMode: clientCtx.TxConfig.SignModeHandler().DefaultMode(), + Signature: nil, + }, + Sequence: accSeqs[i], + } + + sigsV2 = append(sigsV2, sigV2) + } + err := txBuilder.SetSignatures(sigsV2...) + if err != nil { + return nil, err + } + + // Second round: all signer infos are set, so each signer can sign. + sigsV2 = []signing.SignatureV2{} + for i, priv := range privs { + signerData := xauthsigning.SignerData{ + ChainID: chainID, + AccountNumber: accNums[i], + Sequence: accSeqs[i], + } + sigV2, err := tx.SignWithPrivKey( + clientCtx.TxConfig.SignModeHandler().DefaultMode(), signerData, + txBuilder, priv, clientCtx.TxConfig, accSeqs[i]) + if err != nil { + return nil, err + } + + sigsV2 = append(sigsV2, sigV2) + } + err = txBuilder.SetSignatures(sigsV2...) + if err != nil { + return nil, err + } + + return txBuilder.GetTx(), nil +} diff --git a/app/test/std_sdk_test.go b/app/test/std_sdk_test.go index cddb02c9a8..dc54a030d0 100644 --- a/app/test/std_sdk_test.go +++ b/app/test/std_sdk_test.go @@ -9,7 +9,6 @@ import ( "github.com/celestiaorg/celestia-app/v3/app/encoding" "github.com/celestiaorg/celestia-app/v3/app/grpc/tx" "github.com/celestiaorg/celestia-app/v3/pkg/appconsts" - v2 "github.com/celestiaorg/celestia-app/v3/pkg/appconsts/v2" "github.com/celestiaorg/celestia-app/v3/pkg/user" "github.com/celestiaorg/celestia-app/v3/test/util/blobfactory" "github.com/celestiaorg/celestia-app/v3/test/util/testfactory" @@ -350,7 +349,7 @@ func (s *StandardSDKIntegrationTestSuite) TestGRPCQueries() { require.NoError(t, err) got, err := resp.NetworkMinGasPrice.Float64() require.NoError(t, err) - assert.Equal(t, v2.NetworkMinGasPrice, got) + assert.Equal(t, appconsts.DefaultNetworkMinGasPrice, got) }) t.Run("testnode can query local min gas price", func(t *testing.T) { serviceClient := nodeservice.NewServiceClient(s.cctx.GRPCClient) diff --git a/app/test/upgrade_test.go b/app/test/upgrade_test.go index 3180de8c4e..459a33aefc 100644 --- a/app/test/upgrade_test.go +++ b/app/test/upgrade_test.go @@ -146,7 +146,7 @@ func TestAppUpgradeV3(t *testing.T) { // TestAppUpgradeV2 verifies that the all module's params are overridden during an // upgrade from v1 -> v2 and the app version changes correctly. func TestAppUpgradeV2(t *testing.T) { - NetworkMinGasPriceDec, err := sdk.NewDecFromStr(fmt.Sprintf("%f", v2.NetworkMinGasPrice)) + NetworkMinGasPriceDec, err := sdk.NewDecFromStr(fmt.Sprintf("%f", appconsts.DefaultNetworkMinGasPrice)) require.NoError(t, err) tests := []struct { diff --git a/pkg/appconsts/initial_consts.go b/pkg/appconsts/initial_consts.go index 1f97adf951..18cafc969d 100644 --- a/pkg/appconsts/initial_consts.go +++ b/pkg/appconsts/initial_consts.go @@ -17,10 +17,6 @@ const ( // maximum number of bytes allowed in a valid block. DefaultMaxBytes = DefaultGovMaxSquareSize * DefaultGovMaxSquareSize * share.ContinuationSparseShareContentSize - // DefaultGasPerBlobByte is the default gas cost deducted per byte of blob - // included in a PayForBlobs txn - DefaultGasPerBlobByte = 8 - // DefaultMinGasPrice is the default min gas price that gets set in the app.toml file. // The min gas price acts as a filter. Transactions below that limit will not pass // a nodes `CheckTx` and thus not be proposed by that node. @@ -30,6 +26,11 @@ const ( // to unbond in a proof of stake system. Any validator within this // time can be subject to slashing under conditions of misbehavior. DefaultUnbondingTime = 3 * 7 * 24 * time.Hour + + // DefaultNetworkMinGasPrice is used by x/minfee to prevent transactions from being + // included in a block if they specify a gas price lower than this. + // Only applies to app version >= 2 + DefaultNetworkMinGasPrice = 0.000001 // utia ) var DefaultUpperBoundMaxBytes = DefaultSquareSizeUpperBound * DefaultSquareSizeUpperBound * share.ContinuationSparseShareContentSize diff --git a/pkg/appconsts/v2/app_consts.go b/pkg/appconsts/v2/app_consts.go index 2ef7a4075c..d5829e84c5 100644 --- a/pkg/appconsts/v2/app_consts.go +++ b/pkg/appconsts/v2/app_consts.go @@ -4,7 +4,4 @@ const ( Version uint64 = 2 SquareSizeUpperBound int = 128 SubtreeRootThreshold int = 64 - // NetworkMinGasPrice is used by x/minfee to prevent transactions from being - // included in a block if they specify a gas price lower than this. - NetworkMinGasPrice float64 = 0.000001 // utia ) diff --git a/pkg/appconsts/v3/app_consts.go b/pkg/appconsts/v3/app_consts.go index 504360864a..3fa92f49a0 100644 --- a/pkg/appconsts/v3/app_consts.go +++ b/pkg/appconsts/v3/app_consts.go @@ -4,4 +4,6 @@ const ( Version uint64 = 3 SquareSizeUpperBound int = 128 SubtreeRootThreshold int = 64 + TxSizeCostPerByte uint64 = 10 + GasPerBlobByte uint32 = 8 ) diff --git a/pkg/appconsts/versioned_consts.go b/pkg/appconsts/versioned_consts.go index a8d01fdedf..7bb1088a8b 100644 --- a/pkg/appconsts/versioned_consts.go +++ b/pkg/appconsts/versioned_consts.go @@ -1,7 +1,6 @@ package appconsts import ( - v1 "github.com/celestiaorg/celestia-app/v3/pkg/appconsts/v1" v3 "github.com/celestiaorg/celestia-app/v3/pkg/appconsts/v3" ) @@ -18,15 +17,25 @@ const ( // // The rationale for this value is described in more detail in ADR-013. func SubtreeRootThreshold(_ uint64) int { - return v1.SubtreeRootThreshold + return v3.SubtreeRootThreshold } // SquareSizeUpperBound imposes an upper bound on the max effective square size. func SquareSizeUpperBound(_ uint64) int { - return v1.SquareSizeUpperBound + return v3.SquareSizeUpperBound +} + +func TxSizeCostPerByte(_ uint64) uint64 { + return v3.TxSizeCostPerByte +} + +func GasPerBlobByte(_ uint64) uint32 { + return v3.GasPerBlobByte } var ( DefaultSubtreeRootThreshold = SubtreeRootThreshold(LatestVersion) DefaultSquareSizeUpperBound = SquareSizeUpperBound(LatestVersion) + DefaultTxSizeCostPerByte = TxSizeCostPerByte(LatestVersion) + DefaultGasPerBlobByte = GasPerBlobByte(LatestVersion) ) diff --git a/pkg/appconsts/versioned_consts_test.go b/pkg/appconsts/versioned_consts_test.go index 6fb5cfc48d..da29d71241 100644 --- a/pkg/appconsts/versioned_consts_test.go +++ b/pkg/appconsts/versioned_consts_test.go @@ -1,7 +1,6 @@ package appconsts_test import ( - "fmt" "testing" "github.com/stretchr/testify/require" @@ -9,52 +8,69 @@ import ( "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" + v3 "github.com/celestiaorg/celestia-app/v3/pkg/appconsts/v3" ) -func TestSubtreeRootThreshold(t *testing.T) { +func TestVersionedConsts(t *testing.T) { testCases := []struct { - version uint64 - expected int + name string + version uint64 + expectedConstant interface{} + got interface{} }{ { - version: v1.Version, - expected: v1.SubtreeRootThreshold, + name: "SubtreeRootThreshold v1", + version: v1.Version, + expectedConstant: v1.SubtreeRootThreshold, + got: appconsts.SubtreeRootThreshold(v1.Version), }, { - version: v2.Version, - expected: v2.SubtreeRootThreshold, + name: "SubtreeRootThreshold v2", + version: v2.Version, + expectedConstant: v2.SubtreeRootThreshold, + got: appconsts.SubtreeRootThreshold(v2.Version), + }, + { + name: "SubtreeRootThreshold v3", + version: v3.Version, + expectedConstant: v3.SubtreeRootThreshold, + got: appconsts.SubtreeRootThreshold(v3.Version), + }, + { + name: "SquareSizeUpperBound v1", + version: v1.Version, + expectedConstant: v1.SquareSizeUpperBound, + got: appconsts.SquareSizeUpperBound(v1.Version), + }, + { + name: "SquareSizeUpperBound v2", + version: v2.Version, + expectedConstant: v2.SquareSizeUpperBound, + got: appconsts.SquareSizeUpperBound(v2.Version), + }, + { + name: "SquareSizeUpperBound v3", + version: v3.Version, + expectedConstant: v3.SquareSizeUpperBound, + got: appconsts.SquareSizeUpperBound(v3.Version), }, - } - - for _, tc := range testCases { - name := fmt.Sprintf("version %v", tc.version) - t.Run(name, func(t *testing.T) { - got := appconsts.SubtreeRootThreshold(tc.version) - require.Equal(t, tc.expected, got) - }) - } -} - -func TestSquareSizeUpperBound(t *testing.T) { - testCases := []struct { - version uint64 - expected int - }{ { - version: v1.Version, - expected: v1.SquareSizeUpperBound, + name: "TxSizeCostPerByte v3", + version: v3.Version, + expectedConstant: v3.TxSizeCostPerByte, + got: appconsts.TxSizeCostPerByte(v3.Version), }, { - version: v2.Version, - expected: v2.SquareSizeUpperBound, + name: "GasPerBlobByte v3", + version: v3.Version, + expectedConstant: v3.GasPerBlobByte, + got: appconsts.GasPerBlobByte(v3.Version), }, } for _, tc := range testCases { - name := fmt.Sprintf("version %v", tc.version) - t.Run(name, func(t *testing.T) { - got := appconsts.SquareSizeUpperBound(tc.version) - require.Equal(t, tc.expected, got) + t.Run(tc.name, func(t *testing.T) { + require.Equal(t, tc.expectedConstant, tc.got) }) } } diff --git a/specs/src/parameters_v3.md b/specs/src/parameters_v3.md new file mode 100644 index 0000000000..491e4834c1 --- /dev/null +++ b/specs/src/parameters_v3.md @@ -0,0 +1,72 @@ +# Parameters v3 + +The parameters below represent the parameters for app version 3. + +Note that not all of these parameters are changeable via governance. This list +also includes parameter that require a hardfork to change due to being manually +hardcoded in the application or they are blocked by the `x/paramfilter` module. + +## Global parameters + +| Parameter | Default | Summary | Changeable via Governance | +|-------------------|---------|------------------------------------------------------------------------------------------------------------------------|---------------------------| +| MaxBlockSizeBytes | 100MiB | Hardcoded value in CometBFT for the protobuf encoded block. | False | +| MaxSquareSize | 128 | Hardcoded maximum square size determined per shares per row or column for the original data square (not yet extended). | False | + +## Module parameters + +| Module.Parameter | Default | Summary | Changeable via Governance | +|-----------------------------------------------|---------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------|---------------------------| +| auth.MaxMemoCharacters | 256 | Largest allowed size for a memo in bytes. | True | +| auth.SigVerifyCostED25519 | 590 | Gas used to verify Ed25519 signature. | True | +| auth.SigVerifyCostSecp256k1 | 1000 | Gas used to verify secp256k1 signature. | True | +| auth.TxSigLimit | 7 | Max number of signatures allowed in a multisig transaction. | True | +| auth.TxSizeCostPerByte | 10 | Gas used per transaction byte. | False | +| bank.SendEnabled | true | Allow transfers. | False | +| blob.GasPerBlobByte | 8 | Gas used per blob byte. | False | +| blob.GovMaxSquareSize | 64 | Governance parameter for the maximum square size of the original data square. | True | +| consensus.block.MaxBytes | 1974272 bytes (~1.88 MiB) | Governance parameter for the maximum size of the protobuf encoded block. | True | +| consensus.block.MaxGas | -1 | Maximum gas allowed per block (-1 is infinite). | True | +| consensus.block.TimeIotaMs | 1000 | Minimum time added to the time in the header each block. | False | +| consensus.evidence.MaxAgeDuration | 1814400000000000 (21 days) | The maximum age of evidence before it is considered invalid in nanoseconds. This value should be identical to the unbonding period. | True | +| consensus.evidence.MaxAgeNumBlocks | 120960 | The maximum number of blocks before evidence is considered invalid. This value will stop CometBFT from pruning block data. | True | +| consensus.evidence.MaxBytes | 1MiB | Maximum size in bytes used by evidence in a given block. | True | +| consensus.validator.PubKeyTypes | Ed25519 | The type of public key used by validators. | False | +| consensus.Version.AppVersion | 2 | Determines protocol rules used for a given height. Incremented by the application upon an upgrade. | True | +| distribution.BaseProposerReward | 0 | Reward in the mint denomination for proposing a block. | True | +| distribution.BonusProposerReward | 0 | Extra reward in the mint denomination for proposers based on the voting power included in the commit. | True | +| distribution.CommunityTax | 0.02 (2%) | Percentage of the inflation sent to the community pool. | True | +| distribution.WithdrawAddrEnabled | true | Enables delegators to withdraw funds to a different address. | True | +| gov.DepositParams.MaxDepositPeriod | 604800000000000 (1 week) | Maximum period for token holders to deposit on a proposal in nanoseconds. | True | +| gov.DepositParams.MinDeposit | 10_000_000_000 utia (10,000 TIA) | Minimum deposit for a proposal to enter voting period. | True | +| gov.TallyParams.Quorum | 0.334 (33.4%) | Minimum percentage of total stake needed to vote for a result to be considered valid. | True | +| gov.TallyParams.Threshold | 0.50 (50%) | Minimum proportion of Yes votes for proposal to pass. | True | +| gov.TallyParams.VetoThreshold | 0.334 (33.4%) | Minimum value of Veto votes to Total votes ratio for proposal to be vetoed. | True | +| gov.VotingParams.VotingPeriod | 604800000000000 (1 week) | Duration of the voting period in nanoseconds. | True | +| ibc.ClientGenesis.AllowedClients | []string{"06-solomachine", "07-tendermint"} | List of allowed IBC light clients. | True | +| ibc.ConnectionGenesis.MaxExpectedTimePerBlock | 7500000000000 (75 seconds) | Maximum expected time per block in nanoseconds under normal operation. | True | +| ibc.Transfer.ReceiveEnabled | true | Enable receiving tokens via IBC. | True | +| ibc.Transfer.SendEnabled | true | Enable sending tokens via IBC. | True | +| icahost.HostEnabled | True | Enables or disables the Inter-Chain Accounts host module. | True | +| icahost.AllowMessages | [icaAllowMessages] | Defines a list of sdk message typeURLs allowed to be executed on a host chain. | True | +| minfee.NetworkMinGasPrice | 0.000001 utia | All transactions must have a gas price greater than or equal to this value. | True | +| mint.BondDenom | utia | Denomination that is inflated and sent to the distribution module account. | False | +| mint.DisinflationRate | 0.10 (10%) | The rate at which the inflation rate decreases each year. | False | +| mint.InitialInflationRate | 0.08 (8%) | The inflation rate the network starts at. | False | +| mint.TargetInflationRate | 0.015 (1.5%) | The inflation rate that the network aims to stabilize at. | False | +| packetfowardmiddleware.FeePercentage | 0 | % of the forwarded packet amount which will be subtracted and distributed to the community pool. | True | +| slashing.DowntimeJailDuration | 1 min | Duration of time a validator must stay jailed. | True | +| slashing.MinSignedPerWindow | 0.75 (75%) | The percentage of SignedBlocksWindow that must be signed not to get jailed. | True | +| slashing.SignedBlocksWindow | 5000 | The range of blocks used to count for downtime. | True | +| slashing.SlashFractionDoubleSign | 0.02 (2%) | Percentage slashed after a validator is jailed for double signing. | True | +| slashing.SlashFractionDowntime | 0.00 (0%) | Percentage slashed after a validator is jailed for downtime. | True | +| staking.BondDenom | utia | Bondable coin denomination. | False | +| staking.HistoricalEntries | 10000 | Number of historical entries to persist in store. | True | +| staking.MaxEntries | 7 | Maximum number of entries in the redelegation queue. | True | +| staking.MaxValidators | 100 | Maximum number of validators. | True | +| staking.MinCommissionRate | 0.05 (5%) | Minimum commission rate used by all validators. | True | +| staking.UnbondingTime | 1814400 (21 days) | Duration of time for unbonding in seconds. | False | + +Note: none of the mint module parameters are governance modifiable because they have been converted into hardcoded constants. See the x/mint README.md for more details. + +[icaAllowMessages]: https://github.com/rootulp/celestia-app/blob/8caa5807df8d15477554eba953bd056ae72d4503/app/ica_host.go#L3-L18 diff --git a/x/blob/README.md b/x/blob/README.md index 3f1e0484ae..efa1f74858 100644 --- a/x/blob/README.md +++ b/x/blob/README.md @@ -49,6 +49,7 @@ message Params { `GasPerBlobByte` is the amount of gas that is consumed per byte of blob data when a `MsgPayForBlobs` is processed. Currently, the default value is 8. This value is set below that of normal transaction gas consumption, which is 10. +`GasPerBlobByte` was a governance-modifiable parameter in v1 and v2. In app v3 and above, it is a versioned parameter, meaning it can only be changed through hard fork upgrades. #### `GovMaxSquareSize` diff --git a/x/blob/ante/ante.go b/x/blob/ante/ante.go index fe58eae87c..3c5249b291 100644 --- a/x/blob/ante/ante.go +++ b/x/blob/ante/ante.go @@ -1,6 +1,8 @@ package ante import ( + "github.com/celestiaorg/celestia-app/v3/pkg/appconsts" + v2 "github.com/celestiaorg/celestia-app/v3/pkg/appconsts/v2" "github.com/celestiaorg/celestia-app/v3/x/blob/types" "cosmossdk.io/errors" @@ -33,8 +35,12 @@ func (d MinGasPFBDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool // NOTE: here we assume only one PFB per transaction if pfb, ok := m.(*types.MsgPayForBlobs); ok { if gasPerByte == 0 { - // lazily fetch the gas per byte param - gasPerByte = d.k.GasPerBlobByte(ctx) + 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) + } } gasToConsume := pfb.Gas(gasPerByte) if gasToConsume > txGas { diff --git a/x/blob/ante/ante_test.go b/x/blob/ante/ante_test.go index e1f16e2b7a..6be76d77d9 100644 --- a/x/blob/ante/ante_test.go +++ b/x/blob/ante/ante_test.go @@ -1,15 +1,20 @@ package ante_test import ( + "fmt" "testing" "github.com/celestiaorg/celestia-app/v3/app" "github.com/celestiaorg/celestia-app/v3/app/encoding" + "github.com/celestiaorg/celestia-app/v3/pkg/appconsts" + v2 "github.com/celestiaorg/celestia-app/v3/pkg/appconsts/v2" ante "github.com/celestiaorg/celestia-app/v3/x/blob/ante" blob "github.com/celestiaorg/celestia-app/v3/x/blob/types" "github.com/celestiaorg/go-square/v2/share" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + "github.com/tendermint/tendermint/proto/tendermint/version" ) const ( @@ -22,8 +27,9 @@ func TestPFBAnteHandler(t *testing.T) { testCases := []struct { name string pfb *blob.MsgPayForBlobs - txGas uint64 + txGas func(uint32) uint32 gasConsumed uint64 + versions []uint64 wantErr bool }{ { @@ -32,8 +38,11 @@ func TestPFBAnteHandler(t *testing.T) { // 1 share = 512 bytes = 5120 gas BlobSizes: []uint32{uint32(share.AvailableBytesFromSparseShares(1))}, }, - txGas: share.ShareSize * testGasPerBlobByte, + txGas: func(testGasPerBlobByte uint32) uint32 { + return share.ShareSize * testGasPerBlobByte + }, gasConsumed: 0, + versions: []uint64{v2.Version, appconsts.LatestVersion}, wantErr: false, }, { @@ -41,8 +50,11 @@ func TestPFBAnteHandler(t *testing.T) { pfb: &blob.MsgPayForBlobs{ BlobSizes: []uint32{uint32(share.AvailableBytesFromSparseShares(1)), uint32(share.AvailableBytesFromSparseShares(2))}, }, - txGas: 3 * share.ShareSize * testGasPerBlobByte, + txGas: func(testGasPerBlobByte uint32) uint32 { + return 3 * share.ShareSize * testGasPerBlobByte + }, gasConsumed: 0, + versions: []uint64{v2.Version, appconsts.LatestVersion}, wantErr: false, }, { @@ -51,8 +63,11 @@ func TestPFBAnteHandler(t *testing.T) { // 2 share = 1024 bytes = 10240 gas BlobSizes: []uint32{uint32(share.AvailableBytesFromSparseShares(1) + 1)}, }, - txGas: 2*share.ShareSize*testGasPerBlobByte - 1, + txGas: func(testGasPerBlobByte uint32) uint32 { + return 2*share.ShareSize*testGasPerBlobByte - 1 + }, gasConsumed: 0, + versions: []uint64{v2.Version, appconsts.LatestVersion}, wantErr: true, }, { @@ -60,8 +75,11 @@ func TestPFBAnteHandler(t *testing.T) { pfb: &blob.MsgPayForBlobs{ BlobSizes: []uint32{uint32(share.AvailableBytesFromSparseShares(1)), uint32(share.AvailableBytesFromSparseShares(2))}, }, - txGas: 3*share.ShareSize*testGasPerBlobByte - 1, + txGas: func(testGasPerBlobByte uint32) uint32 { + return 3*share.ShareSize*testGasPerBlobByte - 1 + }, gasConsumed: 0, + versions: []uint64{v2.Version, appconsts.LatestVersion}, wantErr: true, }, { @@ -70,8 +88,11 @@ func TestPFBAnteHandler(t *testing.T) { // 1 share = 512 bytes = 5120 gas BlobSizes: []uint32{uint32(share.AvailableBytesFromSparseShares(1))}, }, - txGas: share.ShareSize*testGasPerBlobByte + 10000 - 1, + txGas: func(testGasPerBlobByte uint32) uint32 { + return share.ShareSize*testGasPerBlobByte + 10000 - 1 + }, gasConsumed: 10000, + versions: []uint64{v2.Version, appconsts.LatestVersion}, wantErr: true, }, { @@ -80,26 +101,43 @@ func TestPFBAnteHandler(t *testing.T) { // 1 share = 512 bytes = 5120 gas BlobSizes: []uint32{uint32(share.AvailableBytesFromSparseShares(10))}, }, - txGas: 1000000, + txGas: func(_ uint32) uint32 { + return 1000000 + }, gasConsumed: 10000, + versions: []uint64{v2.Version, appconsts.LatestVersion}, wantErr: false, }, } for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - anteHandler := ante.NewMinGasPFBDecorator(mockBlobKeeper{}) - ctx := sdk.Context{}.WithGasMeter(sdk.NewGasMeter(tc.txGas)).WithIsCheckTx(true) - ctx.GasMeter().ConsumeGas(tc.gasConsumed, "test") - txBuilder := txConfig.NewTxBuilder() - require.NoError(t, txBuilder.SetMsgs(tc.pfb)) - tx := txBuilder.GetTx() - _, err := anteHandler.AnteHandle(ctx, tx, false, func(ctx sdk.Context, _ sdk.Tx, _ bool) (sdk.Context, error) { return ctx, nil }) - if tc.wantErr { - require.Error(t, err) - } else { - require.NoError(t, err) - } - }) + for _, currentVersion := range tc.versions { + t.Run(fmt.Sprintf("%s v%d", tc.name, currentVersion), func(t *testing.T) { + anteHandler := ante.NewMinGasPFBDecorator(mockBlobKeeper{}) + var gasPerBlobByte uint32 + if currentVersion == v2.Version { + gasPerBlobByte = testGasPerBlobByte + } else { + gasPerBlobByte = appconsts.GasPerBlobByte(currentVersion) + } + + ctx := sdk.NewContext(nil, tmproto.Header{ + Version: version.Consensus{ + App: currentVersion, + }, + }, true, nil).WithGasMeter(sdk.NewGasMeter(uint64(tc.txGas(gasPerBlobByte)))).WithIsCheckTx(true) + + ctx.GasMeter().ConsumeGas(tc.gasConsumed, "test") + txBuilder := txConfig.NewTxBuilder() + require.NoError(t, txBuilder.SetMsgs(tc.pfb)) + tx := txBuilder.GetTx() + _, err := anteHandler.AnteHandle(ctx, tx, false, func(ctx sdk.Context, _ sdk.Tx, _ bool) (sdk.Context, error) { return ctx, nil }) + if tc.wantErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } } } diff --git a/x/blob/keeper/gas_test.go b/x/blob/keeper/gas_test.go index 8c7bd534ff..a1e17a2bdd 100644 --- a/x/blob/keeper/gas_test.go +++ b/x/blob/keeper/gas_test.go @@ -3,6 +3,7 @@ package keeper_test import ( "testing" + "github.com/celestiaorg/celestia-app/v3/pkg/appconsts" "github.com/celestiaorg/celestia-app/v3/x/blob/types" "github.com/celestiaorg/go-square/v2/share" sdk "github.com/cosmos/cosmos-sdk/types" @@ -23,33 +24,33 @@ func TestPayForBlobGas(t *testing.T) { { name: "1 byte blob", // occupies 1 share msg: types.MsgPayForBlobs{BlobSizes: []uint32{1}}, - wantGasConsumed: uint64(1*share.ShareSize*types.DefaultGasPerBlobByte + paramLookUpCost), // 1 share * 512 bytes per share * 8 gas per byte + 1060 gas for fetching param = 5156 gas + wantGasConsumed: uint64(1*share.ShareSize*appconsts.GasPerBlobByte(appconsts.LatestVersion) + paramLookUpCost), // 1 share * 512 bytes per share * 8 gas per byte + 1060 gas for fetching param = 5156 gas }, { name: "100 byte blob", // occupies 1 share msg: types.MsgPayForBlobs{BlobSizes: []uint32{100}}, - wantGasConsumed: uint64(1*share.ShareSize*types.DefaultGasPerBlobByte + paramLookUpCost), + wantGasConsumed: uint64(1*share.ShareSize*appconsts.GasPerBlobByte(appconsts.LatestVersion) + paramLookUpCost), }, { name: "1024 byte blob", // occupies 3 shares because share prefix (e.g. namespace, info byte) msg: types.MsgPayForBlobs{BlobSizes: []uint32{1024}}, - wantGasConsumed: uint64(3*share.ShareSize*types.DefaultGasPerBlobByte + paramLookUpCost), // 3 shares * 512 bytes per share * 8 gas per byte + 1060 gas for fetching param = 13348 gas + wantGasConsumed: uint64(3*share.ShareSize*appconsts.GasPerBlobByte(appconsts.LatestVersion) + paramLookUpCost), // 3 shares * 512 bytes per share * 8 gas per byte + 1060 gas for fetching param = 13348 gas }, { name: "3 blobs, 1 share each", msg: types.MsgPayForBlobs{BlobSizes: []uint32{1, 1, 1}}, - wantGasConsumed: uint64(3*share.ShareSize*types.DefaultGasPerBlobByte + paramLookUpCost), // 3 shares * 512 bytes per share * 8 gas per byte + 1060 gas for fetching param = 13348 gas + wantGasConsumed: uint64(3*share.ShareSize*appconsts.GasPerBlobByte(appconsts.LatestVersion) + paramLookUpCost), // 3 shares * 512 bytes per share * 8 gas per byte + 1060 gas for fetching param = 13348 gas }, { name: "3 blobs, 6 shares total", msg: types.MsgPayForBlobs{BlobSizes: []uint32{1024, 800, 100}}, - wantGasConsumed: uint64(6*share.ShareSize*types.DefaultGasPerBlobByte + paramLookUpCost), // 6 shares * 512 bytes per share * 8 gas per byte + 1060 gas for fetching param = 25636 gas + wantGasConsumed: uint64(6*share.ShareSize*appconsts.GasPerBlobByte(appconsts.LatestVersion) + paramLookUpCost), // 6 shares * 512 bytes per share * 8 gas per byte + 1060 gas for fetching param = 25636 gas }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - k, stateStore, _ := CreateKeeper(t) + k, stateStore, _ := CreateKeeper(t, appconsts.LatestVersion) ctx := sdk.NewContext(stateStore, tmproto.Header{}, false, nil) _, err := k.PayForBlobs(sdk.WrapSDKContext(ctx), &tc.msg) require.NoError(t, err) @@ -62,7 +63,7 @@ func TestPayForBlobGas(t *testing.T) { func TestChangingGasParam(t *testing.T) { msg := types.MsgPayForBlobs{BlobSizes: []uint32{1024}} - k, stateStore, _ := CreateKeeper(t) + k, stateStore, _ := CreateKeeper(t, appconsts.LatestVersion) tempCtx := sdk.NewContext(stateStore, tmproto.Header{}, false, nil) ctx1 := sdk.NewContext(stateStore, tmproto.Header{}, false, nil) diff --git a/x/blob/keeper/genesis_test.go b/x/blob/keeper/genesis_test.go index d291deebf8..120f9cb21e 100644 --- a/x/blob/keeper/genesis_test.go +++ b/x/blob/keeper/genesis_test.go @@ -3,6 +3,7 @@ package keeper_test import ( "testing" + "github.com/celestiaorg/celestia-app/v3/pkg/appconsts" "github.com/celestiaorg/celestia-app/v3/x/blob" "github.com/celestiaorg/celestia-app/v3/x/blob/types" "github.com/stretchr/testify/require" @@ -13,7 +14,7 @@ func TestGenesis(t *testing.T) { Params: types.DefaultParams(), } - k, _, ctx := CreateKeeper(t) + k, _, ctx := CreateKeeper(t, appconsts.LatestVersion) blob.InitGenesis(ctx, *k, genesisState) got := blob.ExportGenesis(ctx, *k) require.NotNil(t, got) diff --git a/x/blob/keeper/grpc_query_params_test.go b/x/blob/keeper/grpc_query_params_test.go index 70f60860ee..e8c367cd35 100644 --- a/x/blob/keeper/grpc_query_params_test.go +++ b/x/blob/keeper/grpc_query_params_test.go @@ -3,13 +3,14 @@ package keeper_test import ( "testing" + "github.com/celestiaorg/celestia-app/v3/pkg/appconsts" "github.com/celestiaorg/celestia-app/v3/x/blob/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" ) func TestParamsQuery(t *testing.T) { - keeper, _, ctx := CreateKeeper(t) + keeper, _, ctx := CreateKeeper(t, appconsts.LatestVersion) wctx := sdk.WrapSDKContext(ctx) params := types.DefaultParams() keeper.SetParams(ctx, params) diff --git a/x/blob/keeper/keeper.go b/x/blob/keeper/keeper.go index 72a3fb7605..a7e0f3cc71 100644 --- a/x/blob/keeper/keeper.go +++ b/x/blob/keeper/keeper.go @@ -4,6 +4,8 @@ import ( "context" "fmt" + "github.com/celestiaorg/celestia-app/v3/pkg/appconsts" + v2 "github.com/celestiaorg/celestia-app/v3/pkg/appconsts/v2" "github.com/celestiaorg/celestia-app/v3/x/blob/types" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" @@ -43,7 +45,14 @@ func (k Keeper) Logger(ctx sdk.Context) log.Logger { func (k Keeper) PayForBlobs(goCtx context.Context, msg *types.MsgPayForBlobs) (*types.MsgPayForBlobsResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) - gasToConsume := types.GasToConsume(msg.BlobSizes, k.GasPerBlobByte(ctx)) + // GasPerBlobByte is a versioned param from version 3 onwards. + var gasToConsume uint64 + if ctx.BlockHeader().Version.App <= v2.Version { + gasToConsume = types.GasToConsume(msg.BlobSizes, k.GasPerBlobByte(ctx)) + } else { + gasToConsume = types.GasToConsume(msg.BlobSizes, appconsts.GasPerBlobByte(ctx.BlockHeader().Version.App)) + } + ctx.GasMeter().ConsumeGas(gasToConsume, payForBlobGasDescriptor) err := ctx.EventManager().EmitTypedEvent( diff --git a/x/blob/keeper/keeper_test.go b/x/blob/keeper/keeper_test.go index e3da5cb12a..8fee7f81a8 100644 --- a/x/blob/keeper/keeper_test.go +++ b/x/blob/keeper/keeper_test.go @@ -27,7 +27,7 @@ import ( // TestPayForBlobs verifies the attributes on the emitted event. func TestPayForBlobs(t *testing.T) { - k, _, ctx := CreateKeeper(t) + k, _, ctx := CreateKeeper(t, appconsts.LatestVersion) signer := "celestia15drmhzw5kwgenvemy30rqqqgq52axf5wwrruf7" namespace := share.MustNewV0Namespace(bytes.Repeat([]byte{1}, share.NamespaceVersionZeroIDSize)) namespaces := [][]byte{namespace.Bytes()} @@ -72,7 +72,7 @@ func createMsgPayForBlob(t *testing.T, signer string, namespace share.Namespace, return msg } -func CreateKeeper(t *testing.T) (*keeper.Keeper, store.CommitMultiStore, sdk.Context) { +func CreateKeeper(t *testing.T, version uint64) (*keeper.Keeper, store.CommitMultiStore, sdk.Context) { storeKey := sdk.NewKVStoreKey(paramtypes.StoreKey) tStoreKey := storetypes.NewTransientStoreKey(paramtypes.TStoreKey) @@ -87,7 +87,7 @@ func CreateKeeper(t *testing.T) (*keeper.Keeper, store.CommitMultiStore, sdk.Con ctx := sdk.NewContext(stateStore, tmproto.Header{ Version: tmversion.Consensus{ Block: 1, - App: 1, + App: version, }, }, false, nil) diff --git a/x/blob/keeper/params_test.go b/x/blob/keeper/params_test.go index 9431ef3828..53f5dbca20 100644 --- a/x/blob/keeper/params_test.go +++ b/x/blob/keeper/params_test.go @@ -3,12 +3,13 @@ package keeper_test import ( "testing" + "github.com/celestiaorg/celestia-app/v3/pkg/appconsts" "github.com/celestiaorg/celestia-app/v3/x/blob/types" "github.com/stretchr/testify/require" ) func TestGetParams(t *testing.T) { - k, _, ctx := CreateKeeper(t) + k, _, ctx := CreateKeeper(t, appconsts.LatestVersion) params := types.DefaultParams() k.SetParams(ctx, params) diff --git a/x/blob/types/payforblob.go b/x/blob/types/payforblob.go index 92eca0a4d7..b49eb05394 100644 --- a/x/blob/types/payforblob.go +++ b/x/blob/types/payforblob.go @@ -10,7 +10,6 @@ import ( "github.com/celestiaorg/go-square/v2/share" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth/migrations/legacytx" - auth "github.com/cosmos/cosmos-sdk/x/auth/types" "github.com/tendermint/tendermint/crypto/merkle" "golang.org/x/exp/slices" ) @@ -162,10 +161,9 @@ func EstimateGas(blobSizes []uint32, gasPerByte uint32, txSizeCost uint64) uint6 return GasToConsume(blobSizes, gasPerByte) + (txSizeCost * BytesPerBlobInfo * uint64(len(blobSizes))) + PFBGasFixedCost } -// DefaultEstimateGas runs EstimateGas with the system defaults. The network may change these values -// through governance, thus this function should predominantly be used in testing. +// DefaultEstimateGas runs EstimateGas with the system defaults. func DefaultEstimateGas(blobSizes []uint32) uint64 { - return EstimateGas(blobSizes, appconsts.DefaultGasPerBlobByte, auth.DefaultTxSizeCostPerByte) + return EstimateGas(blobSizes, appconsts.DefaultGasPerBlobByte, appconsts.DefaultTxSizeCostPerByte) } // ValidateBlobNamespace returns an error if the provided namespace is an diff --git a/x/minfee/grpc_query_test.go b/x/minfee/grpc_query_test.go index a7ab0fdfef..9e797ee828 100644 --- a/x/minfee/grpc_query_test.go +++ b/x/minfee/grpc_query_test.go @@ -4,7 +4,7 @@ import ( "testing" "github.com/celestiaorg/celestia-app/v3/app" - v2 "github.com/celestiaorg/celestia-app/v3/pkg/appconsts/v2" + "github.com/celestiaorg/celestia-app/v3/pkg/appconsts" testutil "github.com/celestiaorg/celestia-app/v3/test/util" "github.com/celestiaorg/celestia-app/v3/x/minfee" sdk "github.com/cosmos/cosmos-sdk/types" @@ -24,5 +24,5 @@ func TestQueryNetworkMinGasPrice(t *testing.T) { require.NoError(t, err) // Check the response - require.Equal(t, v2.NetworkMinGasPrice, resp.NetworkMinGasPrice.MustFloat64()) + require.Equal(t, appconsts.DefaultNetworkMinGasPrice, resp.NetworkMinGasPrice.MustFloat64()) } diff --git a/x/minfee/params.go b/x/minfee/params.go index ef17044d7c..2f1edc3ee8 100644 --- a/x/minfee/params.go +++ b/x/minfee/params.go @@ -3,7 +3,7 @@ package minfee import ( "fmt" - v2 "github.com/celestiaorg/celestia-app/v3/pkg/appconsts/v2" + "github.com/celestiaorg/celestia-app/v3/pkg/appconsts" sdk "github.com/cosmos/cosmos-sdk/types" paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" ) @@ -18,7 +18,7 @@ var ( ) func init() { - DefaultNetworkMinGasPriceDec, err := sdk.NewDecFromStr(fmt.Sprintf("%f", v2.NetworkMinGasPrice)) + DefaultNetworkMinGasPriceDec, err := sdk.NewDecFromStr(fmt.Sprintf("%f", appconsts.DefaultNetworkMinGasPrice)) if err != nil { panic(err) }