diff --git a/nodebuilder/state/mocks/api.go b/nodebuilder/state/mocks/api.go index 409766acc8..6f4f9d1a37 100644 --- a/nodebuilder/state/mocks/api.go +++ b/nodebuilder/state/mocks/api.go @@ -221,18 +221,18 @@ func (mr *MockModuleMockRecorder) SubmitPayForBlob(arg0, arg1, arg2, arg3 interf } // SubmitPayForBlobWithOptions mocks base method. -func (m *MockModule) SubmitPayForBlobWithOptions(arg0 context.Context, arg1 math.Int, arg2 uint64, arg3 []*blob.Blob, arg4 *state.PayForBlobOptions) (*types.TxResponse, error) { +func (m *MockModule) SubmitPayForBlobWithOptions(arg0 context.Context, arg1 []*blob.Blob, arg2 *state.TxOptions) (*types.TxResponse, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SubmitPayForBlobWithOptions", arg0, arg1, arg2, arg3, arg4) + ret := m.ctrl.Call(m, "SubmitPayForBlobWithOptions", arg0, arg1, arg2) ret0, _ := ret[0].(*types.TxResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // SubmitPayForBlobWithOptions indicates an expected call of SubmitPayForBlobWithOptions. -func (mr *MockModuleMockRecorder) SubmitPayForBlobWithOptions(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call { +func (mr *MockModuleMockRecorder) SubmitPayForBlobWithOptions(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitPayForBlobWithOptions", reflect.TypeOf((*MockModule)(nil).SubmitPayForBlobWithOptions), arg0, arg1, arg2, arg3, arg4) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitPayForBlobWithOptions", reflect.TypeOf((*MockModule)(nil).SubmitPayForBlobWithOptions), arg0, arg1, arg2) } // SubmitTx mocks base method. diff --git a/nodebuilder/state/state.go b/nodebuilder/state/state.go index 7b128c5f21..0336ee1441 100644 --- a/nodebuilder/state/state.go +++ b/nodebuilder/state/state.go @@ -50,10 +50,8 @@ type Module interface { // SubmitPayForBlobWithOptions builds, signs and submits a PayForBlob transaction with additional options. SubmitPayForBlobWithOptions( ctx context.Context, - fee state.Int, - gasLim uint64, blobs []*blob.Blob, - options *state.PayForBlobOptions, + options *state.TxOptions, ) (*state.TxResponse, error) // CancelUnbondingDelegation cancels a user's pending undelegation from a validator. CancelUnbondingDelegation( @@ -140,10 +138,8 @@ type API struct { ) (*state.TxResponse, error) `perm:"write"` SubmitPayForBlobWithOptions func( ctx context.Context, - fee state.Int, - gasLim uint64, blobs []*blob.Blob, - options *state.PayForBlobOptions, + options *state.TxOptions, ) (*state.TxResponse, error) `perm:"write"` CancelUnbondingDelegation func( ctx context.Context, @@ -237,12 +233,10 @@ func (api *API) SubmitPayForBlob( func (api *API) SubmitPayForBlobWithOptions( ctx context.Context, - fee state.Int, - gasLim uint64, blobs []*blob.Blob, - options *state.PayForBlobOptions, + options *state.TxOptions, ) (*state.TxResponse, error) { - return api.Internal.SubmitPayForBlobWithOptions(ctx, fee, gasLim, blobs, options) + return api.Internal.SubmitPayForBlobWithOptions(ctx, blobs, options) } func (api *API) CancelUnbondingDelegation( diff --git a/nodebuilder/state/stub.go b/nodebuilder/state/stub.go index 7c3a583fcd..77b51d3788 100644 --- a/nodebuilder/state/stub.go +++ b/nodebuilder/state/stub.go @@ -56,10 +56,8 @@ func (s stubbedStateModule) SubmitPayForBlob( func (s stubbedStateModule) SubmitPayForBlobWithOptions( context.Context, - state.Int, - uint64, []*blob.Blob, - *state.PayForBlobOptions, + *state.TxOptions, ) (*state.TxResponse, error) { return nil, ErrNoStateAccess } diff --git a/state/core_access.go b/state/core_access.go index 25e5febe8c..058173b195 100644 --- a/state/core_access.go +++ b/state/core_access.go @@ -9,6 +9,7 @@ import ( "time" sdkErrors "cosmossdk.io/errors" + sdkmath "cosmossdk.io/math" "github.com/cosmos/cosmos-sdk/api/tendermint/abci" nodeservice "github.com/cosmos/cosmos-sdk/client/grpc/node" "github.com/cosmos/cosmos-sdk/crypto/keyring" @@ -225,18 +226,16 @@ func (ca *CoreAccessor) SubmitPayForBlob( gasLim uint64, blobs []*blob.Blob, ) (*TxResponse, error) { - return ca.SubmitPayForBlobWithOptions(ctx, fee, gasLim, blobs, nil) + return ca.SubmitPayForBlobWithOptions(ctx, blobs, &TxOptions{Fee: fee.Int64(), GasLimit: gasLim}) } // SubmitPayForBlobWithOptions builds, signs, and submits a MsgPayForBlob with additional options defined -// in `PayForBlobOptions`. If gasLim is set to 0, the method will automatically estimate the gas limit. +// in `TxOptions`. If gasLim is set to 0, the method will automatically estimate the gas limit. // If the fee is negative, the method will use the nodes min gas price multiplied by the gas limit. func (ca *CoreAccessor) SubmitPayForBlobWithOptions( ctx context.Context, - fee Int, - gasLim uint64, blobs []*blob.Blob, - _ *PayForBlobOptions, + options *TxOptions, ) (*TxResponse, error) { signer, err := ca.getSigner(ctx) if err != nil { @@ -257,7 +256,7 @@ func (ca *CoreAccessor) SubmitPayForBlobWithOptions( // we only estimate gas if the user wants us to (by setting the gasLim to 0). In the future we may // want to make these arguments optional. - if gasLim == 0 { + if options.GasLimit == 0 { blobSizes := make([]uint32, len(blobs)) for i, blob := range blobs { blobSizes[i] = uint32(len(blob.Data)) @@ -266,7 +265,7 @@ func (ca *CoreAccessor) SubmitPayForBlobWithOptions( // TODO (@cmwaters): the default gas per byte and the default tx size cost per byte could be changed // through governance. This section could be more robust by tracking these values and adjusting the // gas limit accordingly (as is done for the gas price) - gasLim = apptypes.EstimateGas(blobSizes, appconsts.DefaultGasPerBlobByte, auth.DefaultTxSizeCostPerByte) + options.GasLimit = apptypes.EstimateGas(blobSizes, appconsts.DefaultGasPerBlobByte, auth.DefaultTxSizeCostPerByte) } minGasPrice := ca.getMinGasPrice() @@ -275,39 +274,41 @@ func (ca *CoreAccessor) SubmitPayForBlobWithOptions( // set granter and update gasLimit in case node run in a grantee mode if !ca.granter.Empty() { feeGrant = user.SetFeeGranter(ca.granter) - gasLim = uint64(float64(gasLim) * gasMultiplier) + options.GasLimit = uint64(float64(options.GasLimit) * gasMultiplier) } + + fee := sdkmath.NewInt(options.Fee) // set the fee for the user as the minimum gas price multiplied by the gas limit estimatedFee := false if fee.IsNegative() { estimatedFee = true - fee = sdktypes.NewInt(int64(math.Ceil(minGasPrice * float64(gasLim)))) + fee = sdktypes.NewInt(int64(math.Ceil(minGasPrice * float64(options.GasLimit)))) } var lastErr error for attempt := 0; attempt < maxRetries; attempt++ { - options := []user.TxOption{user.SetGasLimit(gasLim), withFee(fee)} + opts := []user.TxOption{user.SetGasLimit(options.GasLimit), withFee(fee)} if feeGrant != nil { - options = append(options, feeGrant) + opts = append(opts, feeGrant) } response, err := signer.SubmitPayForBlob( ctx, appblobs, - options..., + opts..., ) // the node is capable of changing the min gas price at any time so we must be able to detect it and // update our version accordingly if apperrors.IsInsufficientMinGasPrice(err) && estimatedFee { // The error message contains enough information to parse the new min gas price - minGasPrice, err = apperrors.ParseInsufficientMinGasPrice(err, minGasPrice, gasLim) + minGasPrice, err = apperrors.ParseInsufficientMinGasPrice(err, minGasPrice, options.GasLimit) if err != nil { return nil, fmt.Errorf("parsing insufficient min gas price error: %w", err) } ca.setMinGasPrice(minGasPrice) lastErr = err // update the fee to retry again - fee = sdktypes.NewInt(int64(math.Ceil(minGasPrice * float64(gasLim)))) + fee = sdktypes.NewInt(int64(math.Ceil(minGasPrice * float64(options.GasLimit)))) continue } diff --git a/state/core_access_test.go b/state/core_access_test.go index 0f9b0f5318..149339db27 100644 --- a/state/core_access_test.go +++ b/state/core_access_test.go @@ -90,6 +90,16 @@ func TestSubmitPayForBlob(t *testing.T) { } }) } + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + resp, err := ca.SubmitPayForBlobWithOptions(ctx, tc.blobs, &TxOptions{Fee: tc.fee.Int64(), GasLimit: tc.gasLim}) + require.Equal(t, tc.expErr, err) + if err == nil { + require.EqualValues(t, 0, resp.Code) + } + }) + } } func extractPort(addr string) string { diff --git a/state/state.go b/state/state.go index 67337626b9..e62ae8fb1f 100644 --- a/state/state.go +++ b/state/state.go @@ -57,9 +57,11 @@ func (a Address) MarshalJSON() ([]byte, error) { return []byte("\"" + a.Address.String() + "\""), nil } -// PayForBlobOptions contains additional options that will be applied to the `MsgPayForBlob`. -type PayForBlobOptions struct { +// TxOptions contains additional options that will be applied to the `MsgPayForBlob`. +type TxOptions struct { // Specifies the key that should be used to sign transactions. // NOTE: This `KeyName` should be available in the `Keystore`. - KeyName string + KeyName string + Fee int64 + GasLimit uint64 }