diff --git a/Dockerfile b/Dockerfile index 6334b1104..0e5e6b243 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,7 +9,7 @@ ARG COMMIT ARG BUILD_TARGET=build # See https://github.com/initia-labs/movevm/releases -ENV LIBMOVEVM_VERSION=v1.2.0 +ENV LIBMOVEVM_VERSION=v1.3.0 ENV MIMALLOC_VERSION=v2.2.2 # Install necessary packages diff --git a/app/ante/ante.go b/app/ante/ante.go index 74071bf7f..9c4140fbf 100644 --- a/app/ante/ante.go +++ b/app/ante/ante.go @@ -63,7 +63,7 @@ func NewAnteHandler(options HandlerOptions) (sdk.AnteHandler, error) { anteDecorators := []sdk.AnteDecorator{ ante.NewSetUpContextDecorator(), // outermost AnteDecorator. SetUpContext must be called first ante.NewExtensionOptionsDecorator(options.ExtensionOptionChecker), - moveante.NewGasPricesDecorator(), + moveante.NewMoveContextDecorator(), dynamicfeeante.NewBlockGasDecorator(options.DynamicFeeKeeper), ante.NewValidateBasicDecorator(), ante.NewTxTimeoutHeightDecorator(), diff --git a/app/ante/minimal.go b/app/ante/minimal.go index 43dd6d8cf..fc92d2394 100644 --- a/app/ante/minimal.go +++ b/app/ante/minimal.go @@ -44,7 +44,7 @@ func NewMinimalAnteHandler(options HandlerOptions) (sdk.AnteHandler, error) { anteDecorators := []sdk.AnteDecorator{ ante.NewSetUpContextDecorator(), ante.NewExtensionOptionsDecorator(options.ExtensionOptionChecker), - moveante.NewGasPricesDecorator(), + moveante.NewMoveContextDecorator(), dynamicfeeante.NewBlockGasDecorator(options.DynamicFeeKeeper), ante.NewValidateBasicDecorator(), ante.NewTxTimeoutHeightDecorator(), diff --git a/go.mod b/go.mod index 400e8791c..c80394c4c 100644 --- a/go.mod +++ b/go.mod @@ -42,7 +42,7 @@ require ( github.com/initia-labs/OPinit/api v1.4.1 github.com/initia-labs/initia/api v1.4.0 // we also need to update `LIBMOVEVM_VERSION` of Dockerfile#11 - github.com/initia-labs/movevm v1.2.0 + github.com/initia-labs/movevm v1.3.0 github.com/initia-labs/store v0.1.2 github.com/noble-assets/forwarding/v2 v2.0.3 github.com/pelletier/go-toml v1.9.5 diff --git a/go.sum b/go.sum index 07d6480be..5b1ee7fd2 100644 --- a/go.sum +++ b/go.sum @@ -572,8 +572,8 @@ github.com/initia-labs/forwarding/v2 v2.0.3-initia.4 h1:jHxKhDxt1Yhg7AVZoCFfABPO github.com/initia-labs/forwarding/v2 v2.0.3-initia.4/go.mod h1:slR65ckL3elajwnJO1RRHbqRFue4YtZrDVFh33Ux6FE= github.com/initia-labs/iavl v1.2.6-initia.1 h1:4vf9eiccLkob2eiM8YAM8Gf/UBniEFUKpvncM/hVoO8= github.com/initia-labs/iavl v1.2.6-initia.1/go.mod h1:GiM43q0pB+uG53mLxLDzimxM9l/5N9UuSY3/D0huuVw= -github.com/initia-labs/movevm v1.2.0 h1:IbpipjRIZe851FJZZzP3RGIqrOCcNsI6bDF9VC0K0do= -github.com/initia-labs/movevm v1.2.0/go.mod h1:sj/kXD7mUQASqogPTjqltIr4Uid3xxnlcbfiFvvqeXA= +github.com/initia-labs/movevm v1.3.0 h1:6kpIuNGDV3ZP9VMxBv+gWl+BWKokX2fMeEcWJfor7Zc= +github.com/initia-labs/movevm v1.3.0/go.mod h1:s+ryMZQ4ef8gEmvLjvHJDz5w9OGyBezfyMiaR0wo+bs= github.com/initia-labs/store v0.1.2 h1:NiTc0uz2dSd/A8biFU88G2+K4yFQhVT7NQwrBsZNps0= github.com/initia-labs/store v0.1.2/go.mod h1:AV8QLhCM1qQH3fywIy08xLqf7GlZMECi54NzBNQ92mk= github.com/initia-labs/store/memiavl v0.1.2 h1:0q+V0x6NO6mwXfbTKJHkwdCbwTU/NsEJF3OjWxMOJPM= diff --git a/x/move/ante/gas_prices.go b/x/move/ante/gas_prices.go deleted file mode 100644 index 69d4f2cf0..000000000 --- a/x/move/ante/gas_prices.go +++ /dev/null @@ -1,45 +0,0 @@ -package ante - -import ( - "cosmossdk.io/errors" - "cosmossdk.io/math" - - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - - movetypes "github.com/initia-labs/initia/x/move/types" -) - -// GasPricesDecorator ante decorator to set gas prices to a context -type GasPricesDecorator struct{} - -// NewGasPricesDecorator constructor of the GasPricesDecorator -func NewGasPricesDecorator() *GasPricesDecorator { - return &GasPricesDecorator{} -} - -// AnteHandle that set gas prices to a context -func (d GasPricesDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) { - feeTx, ok := tx.(sdk.FeeTx) - if !ok { - return ctx, errors.Wrap(sdkerrors.ErrTxDecode, "Tx must be a FeeTx") - } - - feeCoins := feeTx.GetFee() - gas := feeTx.GetGas() - - if !simulate { - if gas == 0 { - return ctx, errors.Wrap(sdkerrors.ErrOutOfGas, "Transaction gas cannot be zero.") - } - - // store a tx gas prices - ctx = ctx.WithValue(movetypes.GasPricesContextKey, sdk.NewDecCoinsFromCoins(feeCoins...).QuoDecTruncate(math.LegacyNewDec(int64(gas)))) //nolint: gosec - } - - if next != nil { - return next(ctx, tx, simulate) - } - - return ctx, nil -} diff --git a/x/move/ante/move_context.go b/x/move/ante/move_context.go new file mode 100644 index 000000000..11224a863 --- /dev/null +++ b/x/move/ante/move_context.go @@ -0,0 +1,64 @@ +package ante + +import ( + "cosmossdk.io/errors" + "cosmossdk.io/math" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + + movetypes "github.com/initia-labs/initia/x/move/types" + + vmtypes "github.com/initia-labs/movevm/types" +) + +// MoveContextDecorator sets Move-related values on the context. +type MoveContextDecorator struct{} + +// NewMoveContextDecorator returns a MoveContextDecorator. +func NewMoveContextDecorator() *MoveContextDecorator { + return &MoveContextDecorator{} +} + +// GasPricesDecorator sets Move-related values on the context. +// +// Deprecated: use MoveContextDecorator. +type GasPricesDecorator = MoveContextDecorator + +// NewGasPricesDecorator returns a MoveContextDecorator. +// +// Deprecated: use NewMoveContextDecorator. +func NewGasPricesDecorator() *MoveContextDecorator { + return NewMoveContextDecorator() +} + +// AnteHandle sets Move-related values on the context. +func (d MoveContextDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) { + feeTx, ok := tx.(sdk.FeeTx) + if !ok { + return ctx, errors.Wrap(sdkerrors.ErrTxDecode, "Tx must be a FeeTx") + } + + if !simulate { + feeCoins := feeTx.GetFee() + gas := feeTx.GetGas() + if gas == 0 { + return ctx, errors.Wrap(sdkerrors.ErrOutOfGas, "Transaction gas cannot be zero.") + } + + // store a tx gas prices + ctx = ctx.WithValue(movetypes.GasPricesContextKey, sdk.NewDecCoinsFromCoins(feeCoins...).QuoDecTruncate(math.LegacyNewDec(int64(gas)))) //nolint: gosec + } + + feePayer := feeTx.FeePayer() + feePayerVMAddr, err := vmtypes.NewAccountAddressFromBytes(feePayer) + if err == nil { + ctx = ctx.WithValue(movetypes.FeePayerContextKey, &feePayerVMAddr) + } + + if next != nil { + return next(ctx, tx, simulate) + } + + return ctx, nil +} diff --git a/x/move/ante/gas_prices_test.go b/x/move/ante/move_context_test.go similarity index 61% rename from x/move/ante/gas_prices_test.go rename to x/move/ante/move_context_test.go index d276f1b90..fecaa26ec 100644 --- a/x/move/ante/gas_prices_test.go +++ b/x/move/ante/move_context_test.go @@ -12,6 +12,8 @@ import ( initiaapp "github.com/initia-labs/initia/app" "github.com/initia-labs/initia/x/move/ante" movetypes "github.com/initia-labs/initia/x/move/types" + + vmtypes "github.com/initia-labs/movevm/types" ) const baseDenom = initiaapp.BondDenom @@ -25,31 +27,46 @@ func (t *TestBlockGasMeter) AccumulateGas(ctx context.Context, gas uint64) error return nil } -func (suite *AnteTestSuite) TestGasPricesDecorator() { +func (suite *AnteTestSuite) requireFeePayer(ctx sdk.Context, expected sdk.AccAddress) { + expectedFeePayer, err := vmtypes.NewAccountAddressFromBytes(expected) + suite.Require().NoError(err) + + actualFeePayer := movetypes.FeePayer(ctx) + suite.Require().NotNil(actualFeePayer) + suite.Require().True(expectedFeePayer.Equals(*actualFeePayer)) +} + +func (suite *AnteTestSuite) TestMoveContextDecorator() { suite.SetupTest() // setup suite.txBuilder = suite.clientCtx.TxConfig.NewTxBuilder() // keys and addresses - priv1, _, _ := testdata.KeyTestPubAddr() + priv1, _, addr1 := testdata.KeyTestPubAddr() + priv2, _, feePayer := testdata.KeyTestPubAddr() + err := suite.txBuilder.SetMsgs(testdata.NewTestMsg(addr1)) + suite.Require().NoError(err) feeAmount := sdk.NewCoins(sdk.NewCoin(baseDenom, math.NewInt(100))) gasLimit := uint64(200_000) suite.txBuilder.SetFeeAmount(feeAmount) suite.txBuilder.SetGasLimit(gasLimit) + suite.txBuilder.SetFeePayer(feePayer) - privs, accNums, accSeqs := []cryptotypes.PrivKey{priv1}, []uint64{0}, []uint64{0} + privs, accNums, accSeqs := []cryptotypes.PrivKey{priv1, priv2}, []uint64{0, 0}, []uint64{0, 0} tx, err := suite.CreateTestTx(privs, accNums, accSeqs, suite.ctx.ChainID()) suite.Require().NoError(err) - decorator := ante.NewGasPricesDecorator() + decorator := ante.NewMoveContextDecorator() // in normal mode ctx, err := decorator.AnteHandle(suite.ctx.WithIsCheckTx(false), tx, false, nil) suite.Require().NoError(err) suite.Require().Equal(sdk.NewDecCoinsFromCoins(feeAmount...).QuoDec(math.LegacyNewDec(int64(gasLimit))), ctx.Value(movetypes.GasPricesContextKey).(sdk.DecCoins)) + suite.requireFeePayer(ctx, feePayer) // in simulation mode ctx, err = decorator.AnteHandle(suite.ctx, tx, true, nil) suite.Require().NoError(err) suite.Require().Nil(ctx.Value(movetypes.GasPricesContextKey)) + suite.requireFeePayer(ctx, feePayer) } diff --git a/x/move/keeper/account_abstraction.go b/x/move/keeper/account_abstraction.go index 510371d74..c9cb5b60f 100644 --- a/x/move/keeper/account_abstraction.go +++ b/x/move/keeper/account_abstraction.go @@ -54,7 +54,7 @@ func (k Keeper) VerifyAccountAbstractionSignature(ctx context.Context, sender st &gasBalance, types.NewVMStore(sdkCtx, k.VMStore), NewApi(k, sdkCtx), - types.NewEnv(sdkCtx, ac, ec), + types.NewEnv(sdkCtx, ac, ec, types.FeePayer(ctx)), signer, abstractionData, ) diff --git a/x/move/keeper/binaries/TxContextTests.mv b/x/move/keeper/binaries/TxContextTests.mv new file mode 100644 index 000000000..8964612f5 Binary files /dev/null and b/x/move/keeper/binaries/TxContextTests.mv differ diff --git a/x/move/keeper/common_test.go b/x/move/keeper/common_test.go index 9b9c3cf08..c3fccc868 100644 --- a/x/move/keeper/common_test.go +++ b/x/move/keeper/common_test.go @@ -500,6 +500,7 @@ var publicKeyAuthenticator []byte var counterModule []byte var clammPoolModule []byte var clammScriptsModule []byte +var txContextTestsModule []byte func init() { basicCoinModule = ReadMoveFile("BasicCoin") @@ -516,6 +517,7 @@ func init() { counterModule = ReadMoveFile("Counter") clammPoolModule = ReadMoveFile("pool") clammScriptsModule = ReadMoveFile("scripts") + txContextTestsModule = ReadMoveFile("TxContextTests") } func ReadMoveFile(filename string) []byte { diff --git a/x/move/keeper/contracts/sources/TxContextTests.move b/x/move/keeper/contracts/sources/TxContextTests.move new file mode 100644 index 000000000..c63559463 --- /dev/null +++ b/x/move/keeper/contracts/sources/TxContextTests.move @@ -0,0 +1,18 @@ +module TestAccount::TxContextTests { + use std::option::Option; + use initia_std::transaction_context; + + struct FeePayerStore has key { + value: Option
, + } + + public entry fun store_fee_payer(sender: &signer) { + let fee_payer = transaction_context::fee_payer(); + move_to(sender, FeePayerStore { value: fee_payer }); + } + + #[view] + public fun read_stored_fee_payer(addr: address): Option
acquires FeePayerStore { + borrow_global(addr).value + } +} diff --git a/x/move/keeper/genesis.go b/x/move/keeper/genesis.go index 37872c9ab..36c746cf8 100644 --- a/x/move/keeper/genesis.go +++ b/x/move/keeper/genesis.go @@ -27,11 +27,7 @@ func (k Keeper) Initialize( } api := NewApi(k, ctx) - env := types.NewEnv( - ctx, - types.NextAccountNumber(ctx, k.authKeeper), - ec, - ) + env := types.NewEnv(ctx, types.NextAccountNumber(ctx, k.authKeeper), ec, nil) modules := make([]vmtypes.Module, len(moduleBytes)) for i, moduleBz := range moduleBytes { diff --git a/x/move/keeper/handler.go b/x/move/keeper/handler.go index 6def55bb8..c1f996af2 100644 --- a/x/move/keeper/handler.go +++ b/x/move/keeper/handler.go @@ -175,7 +175,7 @@ func (k Keeper) executeEntryFunction( &gasBalance, types.NewVMStore(sdkCtx, k.VMStore), NewApi(k, sdkCtx), - types.NewEnv(sdkCtx, ac, ec), + types.NewEnv(sdkCtx, ac, ec, types.FeePayer(ctx)), senders, payload, ) @@ -285,7 +285,7 @@ func (k Keeper) executeScript( &gasBalance, types.NewVMStore(sdkCtx, k.VMStore), NewApi(k, sdkCtx), - types.NewEnv(sdkCtx, ac, ec), + types.NewEnv(sdkCtx, ac, ec, types.FeePayer(ctx)), senders, payload, ) @@ -600,7 +600,7 @@ func (k Keeper) executeViewFunction( &gasBalance, types.NewVMStore(sdkCtx, k.VMStore), NewApi(k, sdkCtx), - types.NewEnv(sdkCtx, ac, ec), + types.NewEnv(sdkCtx, ac, ec, types.FeePayer(ctx)), payload, ) diff --git a/x/move/keeper/transaction_context_test.go b/x/move/keeper/transaction_context_test.go new file mode 100644 index 000000000..f38fecde3 --- /dev/null +++ b/x/move/keeper/transaction_context_test.go @@ -0,0 +1,50 @@ +package keeper_test + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/require" + + vmtypes "github.com/initia-labs/movevm/types" + + "github.com/initia-labs/initia/x/move/types" +) + +func TestExecuteEntryFunctionWithFeePayer(t *testing.T) { + ctx, input := createDefaultTestInput(t) + + err := input.MoveKeeper.PublishModuleBundle( + ctx, + vmtypes.TestAddress, + vmtypes.NewModuleBundle(vmtypes.NewModule(txContextTestsModule)), + types.UpgradePolicy_COMPATIBLE, + ) + require.NoError(t, err) + + feePayer, err := vmtypes.NewAccountAddress("0xcafe") + require.NoError(t, err) + ctx = ctx.WithValue(types.FeePayerContextKey, &feePayer) + + err = input.MoveKeeper.ExecuteEntryFunctionJSON( + ctx, + vmtypes.TestAddress, + vmtypes.TestAddress, + "TxContextTests", + "store_fee_payer", + []vmtypes.TypeTag{}, + nil, + ) + require.NoError(t, err) + + res, _, err := input.MoveKeeper.ExecuteViewFunctionJSON( + ctx, + vmtypes.TestAddress, + "TxContextTests", + "read_stored_fee_payer", + []vmtypes.TypeTag{}, + []string{fmt.Sprintf("\"%s\"", vmtypes.TestAddress)}, + ) + require.NoError(t, err) + require.Equal(t, fmt.Sprintf("\"%s\"", feePayer), res.Ret) +} diff --git a/x/move/types/env.go b/x/move/types/env.go index b21c52c93..c92dee3a7 100644 --- a/x/move/types/env.go +++ b/x/move/types/env.go @@ -12,7 +12,7 @@ import ( vmtypes "github.com/initia-labs/movevm/types" ) -func NewEnv(ctx context.Context, nextAccountNumber uint64, executionCounter uint64) vmtypes.Env { +func NewEnv(ctx context.Context, nextAccountNumber uint64, executionCounter uint64, feePayer *vmtypes.AccountAddress) vmtypes.Env { sdkCtx := sdk.UnwrapSDKContext(ctx) txBytes := sdkCtx.TxBytes() if len(txBytes) == 0 { @@ -33,6 +33,7 @@ func NewEnv(ctx context.Context, nextAccountNumber uint64, executionCounter uint NextAccountNumber: nextAccountNumber, TxHash: txHash, SessionId: sessionID, + FeePayer: feePayer, } } diff --git a/x/move/types/keys.go b/x/move/types/keys.go index cae5e8329..ea5c0e5d9 100644 --- a/x/move/types/keys.go +++ b/x/move/types/keys.go @@ -1,6 +1,7 @@ package types import ( + context "context" "fmt" sdk "github.com/cosmos/cosmos-sdk/types" @@ -139,4 +140,16 @@ type contextKey int const ( GasPricesContextKey contextKey = iota AllowDispatchableContextKey + FeePayerContextKey ) + +// FeePayer extract fee payer from the context +func FeePayer(ctx context.Context) *vmtypes.AccountAddress { + if value := ctx.Value(FeePayerContextKey); value != nil { + if feePayer, ok := value.(*vmtypes.AccountAddress); ok { + return feePayer + } + } + + return nil +}