diff --git a/cmd/node/config/enableEpochs.toml b/cmd/node/config/enableEpochs.toml index 7a2bf067ddd..8ec97f60376 100644 --- a/cmd/node/config/enableEpochs.toml +++ b/cmd/node/config/enableEpochs.toml @@ -242,6 +242,9 @@ # RuntimeCodeSizeFixEnableEpoch represents the epoch when the code size fix in the VM is enabled RuntimeCodeSizeFixEnableEpoch = 1 + # RelayedNonceFixEnableEpoch represents the epoch when the nonce fix for relayed txs is enabled + RelayedNonceFixEnableEpoch = 2 + # BLSMultiSignerEnableEpoch represents the activation epoch for different types of BLS multi-signers BLSMultiSignerEnableEpoch = [ { EnableEpoch = 0, Type = "no-KOSK"}, diff --git a/common/enablers/enableEpochsHandler.go b/common/enablers/enableEpochsHandler.go index b9017bdcd4e..f821bcb4653 100644 --- a/common/enablers/enableEpochsHandler.go +++ b/common/enablers/enableEpochsHandler.go @@ -118,6 +118,7 @@ func (handler *enableEpochsHandler) EpochConfirmed(epoch uint32, _ uint64) { handler.setFlagValue(epoch >= handler.enableEpochsConfig.MaxBlockchainHookCountersEnableEpoch, handler.maxBlockchainHookCountersFlag, "maxBlockchainHookCountersFlag") handler.setFlagValue(epoch >= handler.enableEpochsConfig.WipeSingleNFTLiquidityDecreaseEnableEpoch, handler.wipeSingleNFTLiquidityDecreaseFlag, "wipeSingleNFTLiquidityDecreaseFlag") handler.setFlagValue(epoch >= handler.enableEpochsConfig.AlwaysSaveTokenMetaDataEnableEpoch, handler.alwaysSaveTokenMetaDataFlag, "alwaysSaveTokenMetaDataFlag") + handler.setFlagValue(epoch >= handler.enableEpochsConfig.RelayedNonceFixEnableEpoch, handler.relayedNonceFixFlag, "relayedNonceFixFlag") } func (handler *enableEpochsHandler) setFlagValue(value bool, flag *atomic.Flag, flagName string) { @@ -215,6 +216,11 @@ func (handler *enableEpochsHandler) RefactorPeersMiniBlocksEnableEpoch() uint32 return handler.enableEpochsConfig.RefactorPeersMiniBlocksEnableEpoch } +// RelayedNonceFixEnableEpoch returns the epoch when relayed nonce fix becomes active +func (handler *enableEpochsHandler) RelayedNonceFixEnableEpoch() uint32 { + return handler.enableEpochsConfig.RelayedNonceFixEnableEpoch +} + // IsInterfaceNil returns true if there is no value under the interface func (handler *enableEpochsHandler) IsInterfaceNil() bool { return handler == nil diff --git a/common/enablers/enableEpochsHandler_test.go b/common/enablers/enableEpochsHandler_test.go index 661d684f010..aeb4639241a 100644 --- a/common/enablers/enableEpochsHandler_test.go +++ b/common/enablers/enableEpochsHandler_test.go @@ -1,6 +1,7 @@ package enablers import ( + "math" "testing" "github.com/multiversx/mx-chain-core-go/core/check" @@ -91,6 +92,7 @@ func createEnableEpochsConfig() config.EnableEpochs { WipeSingleNFTLiquidityDecreaseEnableEpoch: 75, AlwaysSaveTokenMetaDataEnableEpoch: 76, RuntimeCodeSizeFixEnableEpoch: 77, + RelayedNonceFixEnableEpoch: 78, } } @@ -129,7 +131,7 @@ func TestNewEnableEpochsHandler_EpochConfirmed(t *testing.T) { handler, _ := NewEnableEpochsHandler(cfg, &epochNotifier.EpochNotifierStub{}) require.False(t, check.IfNil(handler)) - handler.EpochConfirmed(77, 0) + handler.EpochConfirmed(math.MaxUint32, 0) assert.Equal(t, cfg.BlockGasAndFeesReCheckEnableEpoch, handler.BlockGasAndFeesReCheckEnableEpoch()) assert.True(t, handler.IsSCDeployFlagEnabled()) @@ -213,11 +215,12 @@ func TestNewEnableEpochsHandler_EpochConfirmed(t *testing.T) { assert.True(t, handler.IsMaxBlockchainHookCountersFlagEnabled()) assert.True(t, handler.IsAlwaysSaveTokenMetaDataEnabled()) assert.True(t, handler.IsRuntimeCodeSizeFixEnabled()) + assert.True(t, handler.IsRelayedNonceFixEnabled()) }) t.Run("flags with == condition should be set, along with all >=", func(t *testing.T) { t.Parallel() - epoch := uint32(78) + epoch := uint32(math.MaxUint32) cfg := createEnableEpochsConfig() cfg.StakingV2EnableEpoch = epoch cfg.ESDTEnableEpoch = epoch @@ -313,6 +316,7 @@ func TestNewEnableEpochsHandler_EpochConfirmed(t *testing.T) { assert.True(t, handler.IsWipeSingleNFTLiquidityDecreaseEnabled()) assert.True(t, handler.IsAlwaysSaveTokenMetaDataEnabled()) assert.True(t, handler.IsRuntimeCodeSizeFixEnabled()) + assert.True(t, handler.IsRelayedNonceFixEnabled()) }) t.Run("flags with < should be set", func(t *testing.T) { t.Parallel() @@ -408,5 +412,6 @@ func TestNewEnableEpochsHandler_EpochConfirmed(t *testing.T) { assert.False(t, handler.IsWipeSingleNFTLiquidityDecreaseEnabled()) assert.False(t, handler.IsAlwaysSaveTokenMetaDataEnabled()) assert.False(t, handler.IsRuntimeCodeSizeFixEnabled()) + assert.False(t, handler.IsRelayedNonceFixEnabled()) }) } diff --git a/common/enablers/epochFlags.go b/common/enablers/epochFlags.go index fe11469f4bb..9a6d3e6e9f1 100644 --- a/common/enablers/epochFlags.go +++ b/common/enablers/epochFlags.go @@ -90,6 +90,7 @@ type epochFlagsHolder struct { maxBlockchainHookCountersFlag *atomic.Flag wipeSingleNFTLiquidityDecreaseFlag *atomic.Flag alwaysSaveTokenMetaDataFlag *atomic.Flag + relayedNonceFixFlag *atomic.Flag } func newEpochFlagsHolder() *epochFlagsHolder { @@ -179,6 +180,7 @@ func newEpochFlagsHolder() *epochFlagsHolder { maxBlockchainHookCountersFlag: &atomic.Flag{}, wipeSingleNFTLiquidityDecreaseFlag: &atomic.Flag{}, alwaysSaveTokenMetaDataFlag: &atomic.Flag{}, + relayedNonceFixFlag: &atomic.Flag{}, } } @@ -659,3 +661,8 @@ func (holder *epochFlagsHolder) IsWipeSingleNFTLiquidityDecreaseEnabled() bool { func (holder *epochFlagsHolder) IsAlwaysSaveTokenMetaDataEnabled() bool { return holder.alwaysSaveTokenMetaDataFlag.IsSet() } + +// IsRelayedNonceFixEnabled returns true if relayedNonceFixFlag is enabled +func (holder *epochFlagsHolder) IsRelayedNonceFixEnabled() bool { + return holder.relayedNonceFixFlag.IsSet() +} diff --git a/common/interface.go b/common/interface.go index a58b6aa94db..625a6d4ff7e 100644 --- a/common/interface.go +++ b/common/interface.go @@ -327,6 +327,7 @@ type EnableEpochsHandler interface { IsMaxBlockchainHookCountersFlagEnabled() bool IsWipeSingleNFTLiquidityDecreaseEnabled() bool IsAlwaysSaveTokenMetaDataEnabled() bool + IsRelayedNonceFixEnabled() bool IsInterfaceNil() bool } diff --git a/config/epochConfig.go b/config/epochConfig.go index e729f362d91..ef4592a319f 100644 --- a/config/epochConfig.go +++ b/config/epochConfig.go @@ -93,6 +93,7 @@ type EnableEpochs struct { MaxBlockchainHookCountersEnableEpoch uint32 WipeSingleNFTLiquidityDecreaseEnableEpoch uint32 AlwaysSaveTokenMetaDataEnableEpoch uint32 + RelayedNonceFixEnableEpoch uint32 BLSMultiSignerEnableEpoch []MultiSignerConfig } diff --git a/config/tomlConfig_test.go b/config/tomlConfig_test.go index a3cc8c949dc..f46ce8d2514 100644 --- a/config/tomlConfig_test.go +++ b/config/tomlConfig_test.go @@ -684,6 +684,9 @@ func TestEnableEpochConfig(t *testing.T) { # RuntimeMemStoreLimitEnableEpoch represents the epoch when the condition for Runtime MemStore is enabled RuntimeMemStoreLimitEnableEpoch = 63 + # RelayedNonceFixEnableEpoch represents the epoch when the nonce fix for relayed txs is enabled + RelayedNonceFixEnableEpoch = 64 + # MaxNodesChangeEnableEpoch holds configuration for changing the maximum number of nodes and the enabling epoch MaxNodesChangeEnableEpoch = [ { EpochEnable = 44, MaxNumNodes = 2169, NodesToShufflePerShard = 80 }, @@ -779,6 +782,7 @@ func TestEnableEpochConfig(t *testing.T) { AlwaysSaveTokenMetaDataEnableEpoch: 61, RuntimeCodeSizeFixEnableEpoch: 62, RuntimeMemStoreLimitEnableEpoch: 63, + RelayedNonceFixEnableEpoch: 64, BLSMultiSignerEnableEpoch: []MultiSignerConfig{ { EnableEpoch: 0, diff --git a/integrationTests/multiShard/relayedTx/edgecases/edgecases_test.go b/integrationTests/multiShard/relayedTx/edgecases/edgecases_test.go index b81667b041d..246a81fbe15 100644 --- a/integrationTests/multiShard/relayedTx/edgecases/edgecases_test.go +++ b/integrationTests/multiShard/relayedTx/edgecases/edgecases_test.go @@ -11,7 +11,7 @@ import ( "github.com/stretchr/testify/assert" ) -func TestRelayedTransactionInMultiShardEnvironmentWithNormalTxButWrongNonce(t *testing.T) { +func TestRelayedTransactionInMultiShardEnvironmentWithNormalTxButWrongNonceShouldNotIncrementUserAccNonce(t *testing.T) { if testing.Short() { t.Skip("this is not a short test") } @@ -68,7 +68,7 @@ func TestRelayedTransactionInMultiShardEnvironmentWithNormalTxButWrongNonce(t *t for _, player := range players { account := relayedTx.GetUserAccount(nodes, player.Address) assert.True(t, account.GetBalance().Cmp(big.NewInt(0)) == 0) - assert.Equal(t, uint64(nrRoundsToTest)*2, account.GetNonce()) + assert.Equal(t, uint64(0), account.GetNonce()) } expectedBalance := big.NewInt(0).Sub(relayerInitialValue, totalFees) diff --git a/integrationTests/testInitializer.go b/integrationTests/testInitializer.go index a44cf3c6828..edfcf26a070 100644 --- a/integrationTests/testInitializer.go +++ b/integrationTests/testInitializer.go @@ -59,6 +59,7 @@ import ( testStorage "github.com/multiversx/mx-chain-go/testscommon/state" "github.com/multiversx/mx-chain-go/testscommon/statusHandler" statusHandlerMock "github.com/multiversx/mx-chain-go/testscommon/statusHandler" + "github.com/multiversx/mx-chain-go/testscommon/txDataBuilder" "github.com/multiversx/mx-chain-go/trie" "github.com/multiversx/mx-chain-go/trie/hashesHolder" "github.com/multiversx/mx-chain-go/vm" @@ -2538,3 +2539,22 @@ func SaveDelegationContractsList(nodes []*TestProcessorNode) { _, _ = n.AccntState.Commit() } } + +// PrepareRelayedTxDataV1 repares the data for a relayed transaction V1 +func PrepareRelayedTxDataV1(innerTx *transaction.Transaction) []byte { + userTxBytes, _ := TestMarshalizer.Marshal(innerTx) + return []byte(core.RelayedTransaction + "@" + hex.EncodeToString(userTxBytes)) +} + +// PrepareRelayedTxDataV2 prepares the data for a relayed transaction V2 +func PrepareRelayedTxDataV2(innerTx *transaction.Transaction) []byte { + dataBuilder := txDataBuilder.NewBuilder() + txData := dataBuilder. + Func(core.RelayedTransactionV2). + Bytes(innerTx.RcvAddr). + Int64(int64(innerTx.Nonce)). + Bytes(innerTx.Data). + Bytes(innerTx.Signature) + + return txData.ToBytes() +} diff --git a/integrationTests/vm/txsFee/multiShard/relayedBuiltInFunctions_test.go b/integrationTests/vm/txsFee/multiShard/relayedBuiltInFunctions_test.go index 2fdeb14eeb9..56d97f9546b 100644 --- a/integrationTests/vm/txsFee/multiShard/relayedBuiltInFunctions_test.go +++ b/integrationTests/vm/txsFee/multiShard/relayedBuiltInFunctions_test.go @@ -56,7 +56,7 @@ func TestRelayedBuiltInFunctionExecuteOnRelayerAndDstShardShouldWork(t *testing. _, _ = vm.CreateAccount(testContextRelayer.Accounts, relayerAddr, 0, big.NewInt(15000)) - rtxData := utils.PrepareRelayerTxData(innerTx) + rtxData := integrationTests.PrepareRelayedTxDataV1(innerTx) rTxGasLimit := 1 + gasLimit + uint64(len(rtxData)) rtx := vm.CreateTransaction(0, innerTx.Value, relayerAddr, owner, gasPrice, rTxGasLimit, rtxData) diff --git a/integrationTests/vm/txsFee/multiShard/relayedMoveBalance_test.go b/integrationTests/vm/txsFee/multiShard/relayedMoveBalance_test.go index bdb65593459..2dd36161143 100644 --- a/integrationTests/vm/txsFee/multiShard/relayedMoveBalance_test.go +++ b/integrationTests/vm/txsFee/multiShard/relayedMoveBalance_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/multiversx/mx-chain-go/config" + "github.com/multiversx/mx-chain-go/integrationTests" "github.com/multiversx/mx-chain-go/integrationTests/vm" "github.com/multiversx/mx-chain-go/integrationTests/vm/txsFee/utils" vmcommon "github.com/multiversx/mx-chain-vm-common-go" @@ -34,7 +35,7 @@ func TestRelayedMoveBalanceRelayerShard0InnerTxSenderAndReceiverShard1ShouldWork userTx := vm.CreateTransaction(0, big.NewInt(100), sndAddr, rcvAddr, gasPrice, gasLimit, []byte("aaaa")) - rtxData := utils.PrepareRelayerTxData(userTx) + rtxData := integrationTests.PrepareRelayedTxDataV1(userTx) rTxGasLimit := 1 + gasLimit + uint64(len(rtxData)) rtx := vm.CreateTransaction(0, userTx.Value, relayerAddr, sndAddr, gasPrice, rTxGasLimit, rtxData) @@ -80,7 +81,7 @@ func TestRelayedMoveBalanceRelayerAndInnerTxSenderShard0ReceiverShard1(t *testin userTx := vm.CreateTransaction(0, big.NewInt(100), sndAddr, scAddrBytes, gasPrice, gasLimit, nil) - rtxData := utils.PrepareRelayerTxData(userTx) + rtxData := integrationTests.PrepareRelayedTxDataV1(userTx) rTxGasLimit := 1 + gasLimit + uint64(len(rtxData)) rtx := vm.CreateTransaction(0, userTx.Value, relayerAddr, sndAddr, gasPrice, rTxGasLimit, rtxData) @@ -131,7 +132,7 @@ func TestRelayedMoveBalanceExecuteOnSourceAndDestination(t *testing.T) { userTx := vm.CreateTransaction(0, big.NewInt(100), sndAddr, scAddrBytes, gasPrice, gasLimit, nil) - rtxData := utils.PrepareRelayerTxData(userTx) + rtxData := integrationTests.PrepareRelayedTxDataV1(userTx) rTxGasLimit := 1 + gasLimit + uint64(len(rtxData)) rtx := vm.CreateTransaction(0, userTx.Value, relayerAddr, sndAddr, gasPrice, rTxGasLimit, rtxData) @@ -193,7 +194,7 @@ func TestRelayedMoveBalanceExecuteOnSourceAndDestinationRelayerAndInnerTxSenderS userTx := vm.CreateTransaction(0, big.NewInt(100), sndAddr, rcvAddr, gasPrice, gasLimit, nil) - rtxData := utils.PrepareRelayerTxData(userTx) + rtxData := integrationTests.PrepareRelayedTxDataV1(userTx) rTxGasLimit := 1 + gasLimit + uint64(len(rtxData)) rtx := vm.CreateTransaction(0, userTx.Value, relayerAddr, sndAddr, gasPrice, rTxGasLimit, rtxData) @@ -253,7 +254,7 @@ func TestRelayedMoveBalanceRelayerAndInnerTxReceiverShard0SenderShard1(t *testin innerTx := vm.CreateTransaction(0, big.NewInt(100), sndAddr, rcvAddr, gasPrice, gasLimit, nil) - rtxData := utils.PrepareRelayerTxData(innerTx) + rtxData := integrationTests.PrepareRelayedTxDataV1(innerTx) rTxGasLimit := 1 + gasLimit + uint64(len(rtxData)) rtx := vm.CreateTransaction(0, innerTx.Value, relayerAddr, sndAddr, gasPrice, rTxGasLimit, rtxData) @@ -329,7 +330,7 @@ func TestMoveBalanceRelayerShard0InnerTxSenderShard1InnerTxReceiverShard2ShouldW innerTx := vm.CreateTransaction(0, big.NewInt(100), sndAddr, rcvAddr, gasPrice, gasLimit, nil) - rtxData := utils.PrepareRelayerTxData(innerTx) + rtxData := integrationTests.PrepareRelayedTxDataV1(innerTx) rTxGasLimit := 1 + gasLimit + uint64(len(rtxData)) rtx := vm.CreateTransaction(0, innerTx.Value, relayerAddr, sndAddr, gasPrice, rTxGasLimit, rtxData) diff --git a/integrationTests/vm/txsFee/multiShard/relayedScDeploy_test.go b/integrationTests/vm/txsFee/multiShard/relayedScDeploy_test.go index 0a82260631d..82bf9fc370f 100644 --- a/integrationTests/vm/txsFee/multiShard/relayedScDeploy_test.go +++ b/integrationTests/vm/txsFee/multiShard/relayedScDeploy_test.go @@ -8,6 +8,7 @@ import ( "testing" "github.com/multiversx/mx-chain-go/config" + "github.com/multiversx/mx-chain-go/integrationTests" "github.com/multiversx/mx-chain-go/integrationTests/vm" "github.com/multiversx/mx-chain-go/integrationTests/vm/txsFee/utils" "github.com/multiversx/mx-chain-go/integrationTests/vm/wasm" @@ -39,7 +40,7 @@ func TestRelayedSCDeployShouldWork(t *testing.T) { scCode := wasm.GetSCCode(contractPath) userTx := vm.CreateTransaction(0, big.NewInt(0), sndAddr, vm.CreateEmptyAddress(), gasPrice, gasLimit, []byte(wasm.CreateDeployTxData(scCode))) - rtxData := utils.PrepareRelayerTxData(userTx) + rtxData := integrationTests.PrepareRelayedTxDataV1(userTx) rTxGasLimit := 1 + gasLimit + uint64(len(rtxData)) rtx := vm.CreateTransaction(0, big.NewInt(0), relayerAddr, sndAddr, gasPrice, rTxGasLimit, rtxData) diff --git a/integrationTests/vm/txsFee/multiShard/relayedTxScCalls_test.go b/integrationTests/vm/txsFee/multiShard/relayedTxScCalls_test.go index f9849be4720..1c37b9624c3 100644 --- a/integrationTests/vm/txsFee/multiShard/relayedTxScCalls_test.go +++ b/integrationTests/vm/txsFee/multiShard/relayedTxScCalls_test.go @@ -10,6 +10,7 @@ import ( "testing" "github.com/multiversx/mx-chain-go/config" + "github.com/multiversx/mx-chain-go/integrationTests" "github.com/multiversx/mx-chain-go/integrationTests/vm" "github.com/multiversx/mx-chain-go/integrationTests/vm/txsFee/utils" vmcommon "github.com/multiversx/mx-chain-vm-common-go" @@ -57,7 +58,7 @@ func TestRelayedTxScCallMultiShardShouldWork(t *testing.T) { gasLimit := uint64(500) innerTx := vm.CreateTransaction(0, big.NewInt(0), sndAddr, scAddr, gasPrice, gasLimit, []byte("increment")) - rtxData := utils.PrepareRelayerTxData(innerTx) + rtxData := integrationTests.PrepareRelayedTxDataV1(innerTx) rTxGasLimit := 1 + gasLimit + uint64(len(rtxData)) rtx := vm.CreateTransaction(0, innerTx.Value, relayerAddr, sndAddr, gasPrice, rTxGasLimit, rtxData) @@ -162,7 +163,7 @@ func TestRelayedTxScCallMultiShardFailOnInnerTxDst(t *testing.T) { gasLimit := uint64(500) innerTx := vm.CreateTransaction(0, big.NewInt(0), sndAddr, scAddr, gasPrice, gasLimit, []byte("incremeno")) - rtxData := utils.PrepareRelayerTxData(innerTx) + rtxData := integrationTests.PrepareRelayedTxDataV1(innerTx) rTxGasLimit := 1 + gasLimit + uint64(len(rtxData)) rtx := vm.CreateTransaction(0, innerTx.Value, relayerAddr, sndAddr, gasPrice, rTxGasLimit, rtxData) diff --git a/integrationTests/vm/txsFee/relayedAsyncCall_test.go b/integrationTests/vm/txsFee/relayedAsyncCall_test.go index 7cf8c80d82c..39a3dbc8f12 100644 --- a/integrationTests/vm/txsFee/relayedAsyncCall_test.go +++ b/integrationTests/vm/txsFee/relayedAsyncCall_test.go @@ -11,19 +11,42 @@ import ( "testing" "github.com/multiversx/mx-chain-go/config" + "github.com/multiversx/mx-chain-go/integrationTests" "github.com/multiversx/mx-chain-go/integrationTests/vm" "github.com/multiversx/mx-chain-go/integrationTests/vm/txsFee/utils" vmcommon "github.com/multiversx/mx-chain-vm-common-go" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestRelayedAsyncCallShouldWork(t *testing.T) { - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) + senderAddr := []byte("12345678901234567890123456789011") + + t.Run("nonce fix is disabled, should increase the sender's nonce", func(t *testing.T) { + testContext := testRelayedAsyncCallShouldWork(t, config.EnableEpochs{ + RelayedNonceFixEnableEpoch: 100000, + }, senderAddr) + defer testContext.Close() + + senderAccount := getAccount(t, testContext, senderAddr) + assert.Equal(t, uint64(1), senderAccount.GetNonce()) + }) + t.Run("nonce fix is enabled, should still increase the sender's nonce", func(t *testing.T) { + testContext := testRelayedAsyncCallShouldWork(t, config.EnableEpochs{ + RelayedNonceFixEnableEpoch: 0, + }, senderAddr) + defer testContext.Close() + + senderAccount := getAccount(t, testContext, senderAddr) + assert.Equal(t, uint64(1), senderAccount.GetNonce()) + }) +} + +func testRelayedAsyncCallShouldWork(t *testing.T, enableEpochs config.EnableEpochs, senderAddr []byte) *vm.VMTestContext { + testContext, err := vm.CreatePreparedTxProcessorWithVMs(enableEpochs) require.Nil(t, err) - defer testContext.Close() egldBalance := big.NewInt(100000000) - senderAddr := []byte("12345678901234567890123456789011") ownerAddr := []byte("12345678901234567890123456789010") relayerAddr := []byte("12345678901234567890123456789017") @@ -48,7 +71,7 @@ func TestRelayedAsyncCallShouldWork(t *testing.T) { innerTx := vm.CreateTransaction(0, big.NewInt(0), senderAddr, secondSCAddress, gasPrice, gasLimit, []byte("doSomething")) - rtxData := utils.PrepareRelayerTxData(innerTx) + rtxData := integrationTests.PrepareRelayedTxDataV1(innerTx) rTxGasLimit := 1 + gasLimit + uint64(len(rtxData)) rtx := vm.CreateTransaction(0, innerTx.Value, relayerAddr, senderAddr, gasPrice, rTxGasLimit, rtxData) @@ -69,4 +92,6 @@ func TestRelayedAsyncCallShouldWork(t *testing.T) { require.Equal(t, big.NewInt(50001950), testContext.TxFeeHandler.GetAccumulatedFees()) require.Equal(t, big.NewInt(4999988), testContext.TxFeeHandler.GetDeveloperFees()) + + return testContext } diff --git a/integrationTests/vm/txsFee/relayedAsyncESDT_test.go b/integrationTests/vm/txsFee/relayedAsyncESDT_test.go index 15db4d3b331..9ef738063fb 100644 --- a/integrationTests/vm/txsFee/relayedAsyncESDT_test.go +++ b/integrationTests/vm/txsFee/relayedAsyncESDT_test.go @@ -11,6 +11,7 @@ import ( "testing" "github.com/multiversx/mx-chain-go/config" + "github.com/multiversx/mx-chain-go/integrationTests" "github.com/multiversx/mx-chain-go/integrationTests/vm" "github.com/multiversx/mx-chain-go/integrationTests/vm/txsFee/utils" vmcommon "github.com/multiversx/mx-chain-vm-common-go" @@ -54,7 +55,7 @@ func TestRelayedAsyncESDTCallShouldWork(t *testing.T) { innerTx := utils.CreateESDTTransferTx(0, sndAddr, firstSCAddress, token, big.NewInt(5000), gasPrice, gasLimit) innerTx.Data = []byte(string(innerTx.Data) + "@" + hex.EncodeToString([]byte("transferToSecondContractHalf"))) - rtxData := utils.PrepareRelayerTxData(innerTx) + rtxData := integrationTests.PrepareRelayedTxDataV1(innerTx) rTxGasLimit := 1 + gasLimit + uint64(len(rtxData)) rtx := vm.CreateTransaction(0, innerTx.Value, relayerAddr, sndAddr, gasPrice, rTxGasLimit, rtxData) @@ -113,7 +114,7 @@ func TestRelayedAsyncESDTCall_InvalidCallFirstContract(t *testing.T) { innerTx := utils.CreateESDTTransferTx(0, sndAddr, firstSCAddress, token, big.NewInt(5000), gasPrice, gasLimit) innerTx.Data = []byte(string(innerTx.Data) + "@" + hex.EncodeToString([]byte("transferToSecondContractRejected"))) - rtxData := utils.PrepareRelayerTxData(innerTx) + rtxData := integrationTests.PrepareRelayedTxDataV1(innerTx) rTxGasLimit := 1 + gasLimit + uint64(len(rtxData)) rtx := vm.CreateTransaction(0, innerTx.Value, relayerAddr, sndAddr, gasPrice, rTxGasLimit, rtxData) @@ -172,7 +173,7 @@ func TestRelayedAsyncESDTCall_InvalidOutOfGas(t *testing.T) { innerTx := utils.CreateESDTTransferTx(0, sndAddr, firstSCAddress, token, big.NewInt(5000), gasPrice, gasLimit) innerTx.Data = []byte(string(innerTx.Data) + "@" + hex.EncodeToString([]byte("transferToSecondContractHalf"))) - rtxData := utils.PrepareRelayerTxData(innerTx) + rtxData := integrationTests.PrepareRelayedTxDataV1(innerTx) rTxGasLimit := 1 + gasLimit + uint64(len(rtxData)) rtx := vm.CreateTransaction(0, innerTx.Value, relayerAddr, sndAddr, gasPrice, rTxGasLimit, rtxData) diff --git a/integrationTests/vm/txsFee/relayedBuiltInFunctions_test.go b/integrationTests/vm/txsFee/relayedBuiltInFunctions_test.go index 72d972b07ff..0e62c0cd303 100644 --- a/integrationTests/vm/txsFee/relayedBuiltInFunctions_test.go +++ b/integrationTests/vm/txsFee/relayedBuiltInFunctions_test.go @@ -42,7 +42,7 @@ func TestRelayedBuildInFunctionChangeOwnerCallShouldWork(t *testing.T) { _, _ = vm.CreateAccount(testContext.Accounts, relayerAddr, 0, big.NewInt(30000)) - rtxData := utils.PrepareRelayerTxData(innerTx) + rtxData := integrationTests.PrepareRelayedTxDataV1(innerTx) rTxGasLimit := 1 + gasLimit + uint64(len(rtxData)) rtx := vm.CreateTransaction(0, innerTx.Value, relayerAddr, owner, gasPrice, rTxGasLimit, rtxData) @@ -89,7 +89,7 @@ func TestRelayedBuildInFunctionChangeOwnerCallWrongOwnerShouldConsumeGas(t *test _, _ = vm.CreateAccount(testContext.Accounts, relayerAddr, 0, big.NewInt(30000)) - rtxData := utils.PrepareRelayerTxData(innerTx) + rtxData := integrationTests.PrepareRelayedTxDataV1(innerTx) rTxGasLimit := 1 + gasLimit + uint64(len(rtxData)) rtx := vm.CreateTransaction(0, innerTx.Value, relayerAddr, owner, gasPrice, rTxGasLimit, rtxData) @@ -135,7 +135,7 @@ func TestRelayedBuildInFunctionChangeOwnerInvalidAddressShouldConsumeGas(t *test _, _ = vm.CreateAccount(testContext.Accounts, relayerAddr, 0, big.NewInt(30000)) - rtxData := utils.PrepareRelayerTxData(innerTx) + rtxData := integrationTests.PrepareRelayedTxDataV1(innerTx) rTxGasLimit := 1 + gasLimit + uint64(len(rtxData)) rtx := vm.CreateTransaction(0, innerTx.Value, relayerAddr, owner, gasPrice, rTxGasLimit, rtxData) @@ -162,7 +162,28 @@ func TestRelayedBuildInFunctionChangeOwnerInvalidAddressShouldConsumeGas(t *test } func TestRelayedBuildInFunctionChangeOwnerCallInsufficientGasLimitShouldConsumeGas(t *testing.T) { - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) + t.Run("nonce fix is disabled, should increase the sender's nonce", func(t *testing.T) { + testRelayedBuildInFunctionChangeOwnerCallInsufficientGasLimitShouldConsumeGas(t, + config.EnableEpochs{ + RelayedNonceFixEnableEpoch: 1000, + }, + 2) + }) + t.Run("nonce fix is enabled, should still increase the sender's nonce", func(t *testing.T) { + testRelayedBuildInFunctionChangeOwnerCallInsufficientGasLimitShouldConsumeGas(t, + config.EnableEpochs{ + RelayedNonceFixEnableEpoch: 0, + }, + 2) + }) +} + +func testRelayedBuildInFunctionChangeOwnerCallInsufficientGasLimitShouldConsumeGas( + t *testing.T, + enableEpochs config.EnableEpochs, + expectedOwnerNonce uint64, +) { + testContext, err := vm.CreatePreparedTxProcessorWithVMs(enableEpochs) require.Nil(t, err) defer testContext.Close() @@ -180,7 +201,7 @@ func TestRelayedBuildInFunctionChangeOwnerCallInsufficientGasLimitShouldConsumeG _, _ = vm.CreateAccount(testContext.Accounts, relayerAddr, 0, big.NewInt(30000)) - rtxData := utils.PrepareRelayerTxData(innerTx) + rtxData := integrationTests.PrepareRelayedTxDataV1(innerTx) rTxGasLimit := 1 + gasLimit + uint64(len(rtxData)) rtx := vm.CreateTransaction(0, innerTx.Value, relayerAddr, owner, gasPrice, rTxGasLimit, rtxData) @@ -196,7 +217,7 @@ func TestRelayedBuildInFunctionChangeOwnerCallInsufficientGasLimitShouldConsumeG vm.TestAccount(t, testContext.Accounts, relayerAddr, 1, expectedBalanceRelayer) expectedBalance := big.NewInt(89030) - vm.TestAccount(t, testContext.Accounts, owner, 2, expectedBalance) + vm.TestAccount(t, testContext.Accounts, owner, expectedOwnerNonce, expectedBalance) // check accumulated fees accumulatedFees := testContext.TxFeeHandler.GetAccumulatedFees() @@ -225,7 +246,7 @@ func TestRelayedBuildInFunctionChangeOwnerCallOutOfGasShouldConsumeGas(t *testin _, _ = vm.CreateAccount(testContext.Accounts, relayerAddr, 0, big.NewInt(30000)) - rtxData := utils.PrepareRelayerTxData(innerTx) + rtxData := integrationTests.PrepareRelayedTxDataV1(innerTx) rTxGasLimit := 1 + gasLimit + uint64(len(rtxData)) rtx := vm.CreateTransaction(0, innerTx.Value, relayerAddr, owner, gasPrice, rTxGasLimit, rtxData) diff --git a/integrationTests/vm/txsFee/relayedDns_test.go b/integrationTests/vm/txsFee/relayedDns_test.go index 31867df3330..6f440a2bc41 100644 --- a/integrationTests/vm/txsFee/relayedDns_test.go +++ b/integrationTests/vm/txsFee/relayedDns_test.go @@ -11,6 +11,7 @@ import ( "testing" "github.com/multiversx/mx-chain-go/config" + "github.com/multiversx/mx-chain-go/integrationTests" "github.com/multiversx/mx-chain-go/integrationTests/vm" "github.com/multiversx/mx-chain-go/integrationTests/vm/txsFee/utils" vmcommon "github.com/multiversx/mx-chain-vm-common-go" @@ -40,7 +41,7 @@ func TestRelayedTxDnsTransaction_ShouldWork(t *testing.T) { // create user name for sender innerTx := vm.CreateTransaction(0, big.NewInt(0), sndAddr, scAddress, gasPrice, gasLimit, txData) - rtxData := utils.PrepareRelayerTxData(innerTx) + rtxData := integrationTests.PrepareRelayedTxDataV1(innerTx) rTxGasLimit := 1 + gasLimit + uint64(len(rtxData)) rtx := vm.CreateTransaction(0, innerTx.Value, relayerAddr, sndAddr, gasPrice, rTxGasLimit, rtxData) @@ -62,7 +63,7 @@ func TestRelayedTxDnsTransaction_ShouldWork(t *testing.T) { // create user name for receiver innerTx = vm.CreateTransaction(0, big.NewInt(0), rcvAddr, scAddress, gasPrice, gasLimit, txData) - rtxData = utils.PrepareRelayerTxData(innerTx) + rtxData = integrationTests.PrepareRelayedTxDataV1(innerTx) rTxGasLimit = 1 + gasLimit + uint64(len(rtxData)) rtx = vm.CreateTransaction(1, innerTx.Value, relayerAddr, rcvAddr, gasPrice, rTxGasLimit, rtxData) @@ -84,7 +85,7 @@ func TestRelayedTxDnsTransaction_ShouldWork(t *testing.T) { innerTx.SndUserName = sndAddrUserName innerTx.RcvUserName = rcvAddrUserName - rtxData = utils.PrepareRelayerTxData(innerTx) + rtxData = integrationTests.PrepareRelayedTxDataV1(innerTx) rTxGasLimit = 1 + gasLimit + uint64(len(rtxData)) rtx = vm.CreateTransaction(2, innerTx.Value, relayerAddr, sndAddr, gasPrice, rTxGasLimit, rtxData) diff --git a/integrationTests/vm/txsFee/relayedESDT_test.go b/integrationTests/vm/txsFee/relayedESDT_test.go index 80e4b0e0462..3c5ffef15ce 100644 --- a/integrationTests/vm/txsFee/relayedESDT_test.go +++ b/integrationTests/vm/txsFee/relayedESDT_test.go @@ -10,6 +10,7 @@ import ( "testing" "github.com/multiversx/mx-chain-go/config" + "github.com/multiversx/mx-chain-go/integrationTests" "github.com/multiversx/mx-chain-go/integrationTests/vm" "github.com/multiversx/mx-chain-go/integrationTests/vm/txsFee/utils" vmcommon "github.com/multiversx/mx-chain-vm-common-go" @@ -35,7 +36,7 @@ func TestRelayedESDTTransferShouldWork(t *testing.T) { gasLimit := uint64(40) innerTx := utils.CreateESDTTransferTx(0, sndAddr, rcvAddr, token, big.NewInt(100), gasPrice, gasLimit) - rtxData := utils.PrepareRelayerTxData(innerTx) + rtxData := integrationTests.PrepareRelayedTxDataV1(innerTx) rTxGasLimit := 1 + gasLimit + uint64(len(rtxData)) rtx := vm.CreateTransaction(0, innerTx.Value, relayerAddr, sndAddr, gasPrice, rTxGasLimit, rtxData) @@ -81,7 +82,7 @@ func TestTestRelayedESTTransferNotEnoughESTValueShouldConsumeGas(t *testing.T) { gasLimit := uint64(40) innerTx := utils.CreateESDTTransferTx(0, sndAddr, rcvAddr, token, big.NewInt(100000001), gasPrice, gasLimit) - rtxData := utils.PrepareRelayerTxData(innerTx) + rtxData := integrationTests.PrepareRelayedTxDataV1(innerTx) rTxGasLimit := 1 + gasLimit + uint64(len(rtxData)) rtx := vm.CreateTransaction(0, innerTx.Value, relayerAddr, sndAddr, gasPrice, rTxGasLimit, rtxData) diff --git a/integrationTests/vm/txsFee/relayedMoveBalance_test.go b/integrationTests/vm/txsFee/relayedMoveBalance_test.go index e0e52bc1e4b..07cdb9d5932 100644 --- a/integrationTests/vm/txsFee/relayedMoveBalance_test.go +++ b/integrationTests/vm/txsFee/relayedMoveBalance_test.go @@ -4,11 +4,16 @@ import ( "math/big" "testing" + "github.com/multiversx/mx-chain-core-go/data/block" + dataTransaction "github.com/multiversx/mx-chain-core-go/data/transaction" "github.com/multiversx/mx-chain-go/config" + "github.com/multiversx/mx-chain-go/integrationTests" "github.com/multiversx/mx-chain-go/integrationTests/vm" - "github.com/multiversx/mx-chain-go/integrationTests/vm/txsFee/utils" "github.com/multiversx/mx-chain-go/process" + "github.com/multiversx/mx-chain-go/sharding" + "github.com/multiversx/mx-chain-go/testscommon/integrationtests" vmcommon "github.com/multiversx/mx-chain-vm-common-go" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -32,7 +37,7 @@ func TestRelayedMoveBalanceShouldWork(t *testing.T) { // gas consumed = 50 userTx := vm.CreateTransaction(senderNonce, big.NewInt(100), sndAddr, rcvAddr, gasPrice, gasLimit, []byte("aaaa")) - rtxData := utils.PrepareRelayerTxData(userTx) + rtxData := integrationTests.PrepareRelayedTxDataV1(userTx) rTxGasLimit := 1 + gasLimit + uint64(len(rtxData)) rtx := vm.CreateTransaction(0, userTx.Value, relayerAddr, sndAddr, gasPrice, rTxGasLimit, rtxData) @@ -73,7 +78,7 @@ func TestRelayedMoveBalanceInvalidGasLimitShouldConsumeGas(t *testing.T) { _, _ = vm.CreateAccount(testContext.Accounts, relayerAddr, 0, big.NewInt(3000)) - rtxData := utils.PrepareRelayerTxData(userTx) + rtxData := integrationTests.PrepareRelayedTxDataV1(userTx) rTxGasLimit := 2 + userTx.GasLimit + uint64(len(rtxData)) rtx := vm.CreateTransaction(0, userTx.Value, relayerAddr, sndAddr, 1, rTxGasLimit, rtxData) @@ -105,7 +110,7 @@ func TestRelayedMoveBalanceInvalidUserTxShouldConsumeGas(t *testing.T) { _, _ = vm.CreateAccount(testContext.Accounts, relayerAddr, 0, big.NewInt(3000)) - rtxData := utils.PrepareRelayerTxData(userTx) + rtxData := integrationTests.PrepareRelayedTxDataV1(userTx) rTxGasLimit := 1 + userTx.GasLimit + uint64(len(rtxData)) rtx := vm.CreateTransaction(0, userTx.Value, relayerAddr, sndAddr, 1, rTxGasLimit, rtxData) @@ -124,7 +129,9 @@ func TestRelayedMoveBalanceInvalidUserTxShouldConsumeGas(t *testing.T) { } func TestRelayedMoveBalanceInvalidUserTxValueShouldConsumeGas(t *testing.T) { - testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{}) + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{ + RelayedNonceFixEnableEpoch: 1, + }) require.Nil(t, err) defer testContext.Close() @@ -137,7 +144,7 @@ func TestRelayedMoveBalanceInvalidUserTxValueShouldConsumeGas(t *testing.T) { _, _ = vm.CreateAccount(testContext.Accounts, relayerAddr, 0, big.NewInt(3000)) - rtxData := utils.PrepareRelayerTxData(userTx) + rtxData := integrationTests.PrepareRelayedTxDataV1(userTx) rTxGasLimit := 1 + userTx.GasLimit + uint64(len(rtxData)) rtx := vm.CreateTransaction(0, big.NewInt(100), relayerAddr, sndAddr, 1, rTxGasLimit, rtxData) @@ -154,3 +161,186 @@ func TestRelayedMoveBalanceInvalidUserTxValueShouldConsumeGas(t *testing.T) { accumulatedFees := testContext.TxFeeHandler.GetAccumulatedFees() require.Equal(t, big.NewInt(275), accumulatedFees) } + +func TestRelayedMoveBalanceHigherNonce(t *testing.T) { + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{ + RelayedNonceFixEnableEpoch: 1, + }) + require.Nil(t, err) + defer testContext.Close() + + relayerAddr := []byte("12345678901234567890123456789033") + sndAddr := []byte("12345678901234567890123456789012") + rcvAddr := []byte("12345678901234567890123456789022") + + _, _ = vm.CreateAccount(testContext.Accounts, sndAddr, 0, big.NewInt(0)) + _, _ = vm.CreateAccount(testContext.Accounts, relayerAddr, 0, big.NewInt(3000)) + userTx := vm.CreateTransaction(100, big.NewInt(150), sndAddr, rcvAddr, 1, 100, nil) + + t.Run("inactive flag should increment", func(t *testing.T) { + initialSenderNonce := getAccount(t, testContext, sndAddr).GetNonce() + + rtxDataV1 := integrationTests.PrepareRelayedTxDataV1(userTx) + executeRelayedTransaction(t, testContext, relayerAddr, userTx, rtxDataV1, big.NewInt(100), sndAddr, vmcommon.UserError) + + senderAccount := getAccount(t, testContext, sndAddr) + require.NotNil(t, senderAccount) + assert.Equal(t, initialSenderNonce+1, senderAccount.GetNonce()) + + rtxDataV2 := integrationTests.PrepareRelayedTxDataV2(userTx) + executeRelayedTransaction(t, testContext, relayerAddr, userTx, rtxDataV2, big.NewInt(0), sndAddr, vmcommon.UserError) + + senderAccount = getAccount(t, testContext, sndAddr) + require.NotNil(t, senderAccount) + assert.Equal(t, initialSenderNonce+2, senderAccount.GetNonce()) + }) + t.Run("active flag should not increment", func(t *testing.T) { + testContext.EpochNotifier.CheckEpoch(&block.Header{Epoch: 1}) + initialSenderNonce := getAccount(t, testContext, sndAddr).GetNonce() + + rtxDataV1 := integrationTests.PrepareRelayedTxDataV1(userTx) + executeRelayedTransaction(t, testContext, relayerAddr, userTx, rtxDataV1, big.NewInt(100), sndAddr, vmcommon.UserError) + + senderAccount := getAccount(t, testContext, sndAddr) + require.NotNil(t, senderAccount) + assert.Equal(t, initialSenderNonce, senderAccount.GetNonce()) + + rtxDataV2 := integrationTests.PrepareRelayedTxDataV2(userTx) + executeRelayedTransaction(t, testContext, relayerAddr, userTx, rtxDataV2, big.NewInt(0), sndAddr, vmcommon.UserError) + + senderAccount = getAccount(t, testContext, sndAddr) + require.NotNil(t, senderAccount) + assert.Equal(t, initialSenderNonce, senderAccount.GetNonce()) + }) +} + +func TestRelayedMoveBalanceLowerNonce(t *testing.T) { + testContext, err := vm.CreatePreparedTxProcessorWithVMs(config.EnableEpochs{ + RelayedNonceFixEnableEpoch: 1, + }) + require.Nil(t, err) + defer testContext.Close() + + relayerAddr := []byte("12345678901234567890123456789033") + sndAddr := []byte("12345678901234567890123456789012") + rcvAddr := []byte("12345678901234567890123456789022") + + _, _ = vm.CreateAccount(testContext.Accounts, sndAddr, 5, big.NewInt(0)) + _, _ = vm.CreateAccount(testContext.Accounts, relayerAddr, 0, big.NewInt(3000)) + userTx := vm.CreateTransaction(4, big.NewInt(150), sndAddr, rcvAddr, 1, 100, nil) + + t.Run("inactive flag should increment", func(t *testing.T) { + initialSenderNonce := getAccount(t, testContext, sndAddr).GetNonce() + + rtxDataV1 := integrationTests.PrepareRelayedTxDataV1(userTx) + executeRelayedTransaction(t, testContext, relayerAddr, userTx, rtxDataV1, big.NewInt(100), sndAddr, vmcommon.UserError) + + senderAccount := getAccount(t, testContext, sndAddr) + require.NotNil(t, senderAccount) + assert.Equal(t, initialSenderNonce+1, senderAccount.GetNonce()) + + rtxDataV2 := integrationTests.PrepareRelayedTxDataV2(userTx) + executeRelayedTransaction(t, testContext, relayerAddr, userTx, rtxDataV2, big.NewInt(0), sndAddr, vmcommon.UserError) + + senderAccount = getAccount(t, testContext, sndAddr) + require.NotNil(t, senderAccount) + assert.Equal(t, initialSenderNonce+2, senderAccount.GetNonce()) + }) + t.Run("active flag should not increment", func(t *testing.T) { + testContext.EpochNotifier.CheckEpoch(&block.Header{Epoch: 1}) + initialSenderNonce := getAccount(t, testContext, sndAddr).GetNonce() + + rtxDataV1 := integrationTests.PrepareRelayedTxDataV1(userTx) + executeRelayedTransaction(t, testContext, relayerAddr, userTx, rtxDataV1, big.NewInt(100), sndAddr, vmcommon.UserError) + + senderAccount := getAccount(t, testContext, sndAddr) + require.NotNil(t, senderAccount) + assert.Equal(t, initialSenderNonce, senderAccount.GetNonce()) + + rtxDataV2 := integrationTests.PrepareRelayedTxDataV2(userTx) + executeRelayedTransaction(t, testContext, relayerAddr, userTx, rtxDataV2, big.NewInt(0), sndAddr, vmcommon.UserError) + + senderAccount = getAccount(t, testContext, sndAddr) + require.NotNil(t, senderAccount) + assert.Equal(t, initialSenderNonce, senderAccount.GetNonce()) + }) +} + +func TestRelayedMoveBalanceHigherNonceWithActivatedFixCrossShard(t *testing.T) { + enableEpochs := config.EnableEpochs{ + RelayedNonceFixEnableEpoch: 0, + } + + shardCoordinator0, _ := sharding.NewMultiShardCoordinator(2, 0) + testContext0, err := vm.CreatePreparedTxProcessorWithVMsWithShardCoordinatorDBAndGas( + enableEpochs, + shardCoordinator0, + integrationtests.CreateMemUnit(), + vm.CreateMockGasScheduleNotifier(), + ) + require.Nil(t, err) + + shardCoordinator1, _ := sharding.NewMultiShardCoordinator(2, 1) + testContext1, err := vm.CreatePreparedTxProcessorWithVMsWithShardCoordinatorDBAndGas( + enableEpochs, + shardCoordinator1, + integrationtests.CreateMemUnit(), + vm.CreateMockGasScheduleNotifier(), + ) + require.Nil(t, err) + defer testContext0.Close() + defer testContext1.Close() + + relayerAddr := []byte("relayer-000000000000000000000000") + assert.Equal(t, uint32(0), shardCoordinator0.ComputeId(relayerAddr)) // shard 0 + sndAddr := []byte("sender-1111111111111111111111111") + assert.Equal(t, uint32(1), shardCoordinator0.ComputeId(sndAddr)) // shard 1 + rcvAddr := []byte("receiver-22222222222222222222222") + assert.Equal(t, uint32(0), shardCoordinator0.ComputeId(rcvAddr)) // shard 0 + + _, _ = vm.CreateAccount(testContext0.Accounts, relayerAddr, 0, big.NewInt(3000)) // create relayer in shard 0 + _, _ = vm.CreateAccount(testContext1.Accounts, sndAddr, 0, big.NewInt(0)) // create sender in shard 1 + + userTx := vm.CreateTransaction(1, big.NewInt(150), sndAddr, rcvAddr, 1, 100, nil) + initialSenderNonce := getAccount(t, testContext1, sndAddr).GetNonce() + + rtxDataV1 := integrationTests.PrepareRelayedTxDataV1(userTx) + executeRelayedTransaction(t, testContext0, relayerAddr, userTx, rtxDataV1, big.NewInt(100), sndAddr, vmcommon.Ok) + + results := testContext0.GetIntermediateTransactions(t) + assert.Equal(t, 0, len(results)) // no scrs, the exact relayed tx will be executed on the receiver shard + + executeRelayedTransaction(t, testContext1, relayerAddr, userTx, rtxDataV1, big.NewInt(100), sndAddr, vmcommon.UserError) + + senderAccount := getAccount(t, testContext1, sndAddr) + require.NotNil(t, senderAccount) + assert.Equal(t, initialSenderNonce, senderAccount.GetNonce()) +} + +func executeRelayedTransaction( + tb testing.TB, + testContext *vm.VMTestContext, + relayerAddress []byte, + userTx *dataTransaction.Transaction, + userTxPrepared []byte, + value *big.Int, + senderAddress []byte, + expectedReturnCode vmcommon.ReturnCode, +) { + relayerAccount := getAccount(tb, testContext, relayerAddress) + gasLimit := 1 + userTx.GasLimit + uint64(len(userTxPrepared)) + + relayedTx := vm.CreateTransaction(relayerAccount.GetNonce(), value, relayerAddress, senderAddress, 1, gasLimit, userTxPrepared) + retCode, _ := testContext.TxProcessor.ProcessTransaction(relayedTx) + require.Equal(tb, expectedReturnCode, retCode) + + _, err := testContext.Accounts.Commit() + require.Nil(tb, err) +} + +func getAccount(tb testing.TB, testContext *vm.VMTestContext, address []byte) vmcommon.UserAccountHandler { + account, err := testContext.Accounts.LoadAccount(address) + require.Nil(tb, err) + + return account.(vmcommon.UserAccountHandler) +} diff --git a/integrationTests/vm/txsFee/relayedScCalls_test.go b/integrationTests/vm/txsFee/relayedScCalls_test.go index 24b1ca74787..45736d7771e 100644 --- a/integrationTests/vm/txsFee/relayedScCalls_test.go +++ b/integrationTests/vm/txsFee/relayedScCalls_test.go @@ -11,9 +11,11 @@ import ( "testing" "github.com/multiversx/mx-chain-go/config" + "github.com/multiversx/mx-chain-go/integrationTests" "github.com/multiversx/mx-chain-go/integrationTests/vm" "github.com/multiversx/mx-chain-go/integrationTests/vm/txsFee/utils" vmcommon "github.com/multiversx/mx-chain-vm-common-go" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -35,7 +37,7 @@ func TestRelayedScCallShouldWork(t *testing.T) { userTx := vm.CreateTransaction(0, big.NewInt(100), sndAddr, scAddress, gasPrice, gasLimit, []byte("increment")) - rtxData := utils.PrepareRelayerTxData(userTx) + rtxData := integrationTests.PrepareRelayedTxDataV1(userTx) rTxGasLimit := 1 + gasLimit + uint64(len(rtxData)) rtx := vm.CreateTransaction(0, userTx.Value, relayerAddr, sndAddr, gasPrice, rTxGasLimit, rtxData) @@ -78,7 +80,7 @@ func TestRelayedScCallContractNotFoundShouldConsumeGas(t *testing.T) { userTx := vm.CreateTransaction(0, big.NewInt(100), sndAddr, scAddrBytes, gasPrice, gasLimit, []byte("increment")) - rtxData := utils.PrepareRelayerTxData(userTx) + rtxData := integrationTests.PrepareRelayedTxDataV1(userTx) rTxGasLimit := 1 + gasLimit + uint64(len(rtxData)) rtx := vm.CreateTransaction(0, userTx.Value, relayerAddr, sndAddr, gasPrice, rTxGasLimit, rtxData) @@ -118,7 +120,7 @@ func TestRelayedScCallInvalidMethodShouldConsumeGas(t *testing.T) { userTx := vm.CreateTransaction(0, big.NewInt(100), sndAddr, scAddress, gasPrice, gasLimit, []byte("invalidMethod")) - rtxData := utils.PrepareRelayerTxData(userTx) + rtxData := integrationTests.PrepareRelayedTxDataV1(userTx) rTxGasLimit := 1 + gasLimit + uint64(len(rtxData)) rtx := vm.CreateTransaction(0, userTx.Value, relayerAddr, sndAddr, gasPrice, rTxGasLimit, rtxData) @@ -158,7 +160,7 @@ func TestRelayedScCallInsufficientGasLimitShouldConsumeGas(t *testing.T) { userTx := vm.CreateTransaction(0, big.NewInt(100), sndAddr, scAddress, gasPrice, gasLimit, []byte("increment")) - rtxData := utils.PrepareRelayerTxData(userTx) + rtxData := integrationTests.PrepareRelayedTxDataV1(userTx) rTxGasLimit := 1 + gasLimit + uint64(len(rtxData)) rtx := vm.CreateTransaction(0, userTx.Value, relayerAddr, sndAddr, gasPrice, rTxGasLimit, rtxData) @@ -197,7 +199,7 @@ func TestRelayedScCallOutOfGasShouldConsumeGas(t *testing.T) { userTx := vm.CreateTransaction(0, big.NewInt(100), sndAddr, scAddress, gasPrice, gasLimit, []byte("increment")) - rtxData := utils.PrepareRelayerTxData(userTx) + rtxData := integrationTests.PrepareRelayedTxDataV1(userTx) rTxGasLimit := 1 + gasLimit + uint64(len(rtxData)) rtx := vm.CreateTransaction(0, userTx.Value, relayerAddr, sndAddr, gasPrice, rTxGasLimit, rtxData) @@ -218,3 +220,74 @@ func TestRelayedScCallOutOfGasShouldConsumeGas(t *testing.T) { developerFees := testContext.TxFeeHandler.GetDeveloperFees() require.Equal(t, big.NewInt(368), developerFees) } + +func TestRelayedDeployInvalidContractShouldIncrementNonceOnSender(t *testing.T) { + senderAddr := []byte("12345678901234567890123456789011") + + t.Run("nonce fix is disabled, should increase the sender's nonce if inner tx has correct nonce", func(t *testing.T) { + testContext := testRelayedDeployInvalidContractShouldIncrementNonceOnSender(t, config.EnableEpochs{ + RelayedNonceFixEnableEpoch: 100000, + }, + senderAddr, + 0) + defer testContext.Close() + + senderAccount := getAccount(t, testContext, senderAddr) + assert.Equal(t, uint64(1), senderAccount.GetNonce()) + }) + t.Run("nonce fix is enabled, should still increase the sender's nonce if inner tx has correct nonce", func(t *testing.T) { + testContext := testRelayedDeployInvalidContractShouldIncrementNonceOnSender(t, config.EnableEpochs{ + RelayedNonceFixEnableEpoch: 0, + }, + senderAddr, + 0) + defer testContext.Close() + + senderAccount := getAccount(t, testContext, senderAddr) + assert.Equal(t, uint64(1), senderAccount.GetNonce()) + }) + t.Run("nonce fix is enabled, should not increase the sender's nonce if inner tx has higher nonce", func(t *testing.T) { + testContext := testRelayedDeployInvalidContractShouldIncrementNonceOnSender(t, config.EnableEpochs{ + RelayedNonceFixEnableEpoch: 0, + }, + senderAddr, + 1) // higher nonce, the current is 0 + defer testContext.Close() + + senderAccount := getAccount(t, testContext, senderAddr) + assert.Equal(t, uint64(0), senderAccount.GetNonce()) + }) +} + +func testRelayedDeployInvalidContractShouldIncrementNonceOnSender( + t *testing.T, + enableEpochs config.EnableEpochs, + senderAddr []byte, + senderNonce uint64, +) *vm.VMTestContext { + testContext, err := vm.CreatePreparedTxProcessorWithVMs(enableEpochs) + require.Nil(t, err) + + relayerAddr := []byte("12345678901234567890123456789033") + gasPrice := uint64(10) + gasLimit := uint64(20) + + _, _ = vm.CreateAccount(testContext.Accounts, senderAddr, 0, big.NewInt(0)) + _, _ = vm.CreateAccount(testContext.Accounts, relayerAddr, 0, big.NewInt(30000)) + + emptyAddress := make([]byte, len(senderAddr)) + userTx := vm.CreateTransaction(senderNonce, big.NewInt(100), senderAddr, emptyAddress, gasPrice, gasLimit, nil) + + rtxData := integrationTests.PrepareRelayedTxDataV1(userTx) + rTxGasLimit := 1 + gasLimit + uint64(len(rtxData)) + rtx := vm.CreateTransaction(0, userTx.Value, relayerAddr, senderAddr, gasPrice, rTxGasLimit, rtxData) + + retCode, err := testContext.TxProcessor.ProcessTransaction(rtx) + require.Equal(t, vmcommon.UserError, retCode) + require.Nil(t, err) + + _, err = testContext.Accounts.Commit() + require.Nil(t, err) + + return testContext +} diff --git a/integrationTests/vm/txsFee/relayedScDeploy_test.go b/integrationTests/vm/txsFee/relayedScDeploy_test.go index d56071d61ec..d1eac0ce8d0 100644 --- a/integrationTests/vm/txsFee/relayedScDeploy_test.go +++ b/integrationTests/vm/txsFee/relayedScDeploy_test.go @@ -10,8 +10,8 @@ import ( "testing" "github.com/multiversx/mx-chain-go/config" + "github.com/multiversx/mx-chain-go/integrationTests" "github.com/multiversx/mx-chain-go/integrationTests/vm" - "github.com/multiversx/mx-chain-go/integrationTests/vm/txsFee/utils" "github.com/multiversx/mx-chain-go/integrationTests/vm/wasm" vmcommon "github.com/multiversx/mx-chain-vm-common-go" "github.com/stretchr/testify/require" @@ -36,7 +36,7 @@ func TestRelayedScDeployShouldWork(t *testing.T) { scCode := wasm.GetSCCode("../wasm/testdata/misc/fib_wasm/output/fib_wasm.wasm") userTx := vm.CreateTransaction(senderNonce, big.NewInt(0), sndAddr, vm.CreateEmptyAddress(), gasPrice, gasLimit, []byte(wasm.CreateDeployTxData(scCode))) - rtxData := utils.PrepareRelayerTxData(userTx) + rtxData := integrationTests.PrepareRelayedTxDataV1(userTx) rTxGasLimit := 1 + gasLimit + uint64(len(rtxData)) rtx := vm.CreateTransaction(0, big.NewInt(0), relayerAddr, sndAddr, gasPrice, rTxGasLimit, rtxData) @@ -79,7 +79,7 @@ func TestRelayedScDeployInvalidCodeShouldConsumeGas(t *testing.T) { scCodeBytes = append(scCodeBytes, []byte("aaaaa")...) userTx := vm.CreateTransaction(senderNonce, big.NewInt(0), sndAddr, vm.CreateEmptyAddress(), gasPrice, gasLimit, scCodeBytes) - rtxData := utils.PrepareRelayerTxData(userTx) + rtxData := integrationTests.PrepareRelayedTxDataV1(userTx) rTxGasLimit := 1 + gasLimit + uint64(len(rtxData)) rtx := vm.CreateTransaction(0, big.NewInt(0), relayerAddr, sndAddr, gasPrice, rTxGasLimit, rtxData) @@ -119,7 +119,7 @@ func TestRelayedScDeployInsufficientGasLimitShouldConsumeGas(t *testing.T) { scCode := wasm.GetSCCode("../wasm/testdata/misc/fib_wasm/output/fib_wasm.wasm") userTx := vm.CreateTransaction(senderNonce, big.NewInt(0), sndAddr, vm.CreateEmptyAddress(), gasPrice, gasLimit, []byte(wasm.CreateDeployTxData(scCode))) - rtxData := utils.PrepareRelayerTxData(userTx) + rtxData := integrationTests.PrepareRelayedTxDataV1(userTx) rTxGasLimit := 1 + gasLimit + uint64(len(rtxData)) rtx := vm.CreateTransaction(0, big.NewInt(0), relayerAddr, sndAddr, gasPrice, rTxGasLimit, rtxData) @@ -159,7 +159,7 @@ func TestRelayedScDeployOutOfGasShouldConsumeGas(t *testing.T) { scCode := wasm.GetSCCode("../wasm/testdata/misc/fib_wasm/output/fib_wasm.wasm") userTx := vm.CreateTransaction(senderNonce, big.NewInt(0), sndAddr, vm.CreateEmptyAddress(), gasPrice, gasLimit, []byte(wasm.CreateDeployTxData(scCode))) - rtxData := utils.PrepareRelayerTxData(userTx) + rtxData := integrationTests.PrepareRelayedTxDataV1(userTx) rTxGasLimit := 1 + gasLimit + uint64(len(rtxData)) rtx := vm.CreateTransaction(0, big.NewInt(0), relayerAddr, sndAddr, gasPrice, rTxGasLimit, rtxData) diff --git a/integrationTests/vm/txsFee/utils/utils.go b/integrationTests/vm/txsFee/utils/utils.go index 1d9e114d8c2..8947805befa 100644 --- a/integrationTests/vm/txsFee/utils/utils.go +++ b/integrationTests/vm/txsFee/utils/utils.go @@ -11,7 +11,6 @@ import ( "strings" "testing" - "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-core-go/data" "github.com/multiversx/mx-chain-core-go/data/scheduled" "github.com/multiversx/mx-chain-core-go/data/smartContractResult" @@ -257,12 +256,6 @@ func DoDeployDNS(t *testing.T, testContext *vm.VMTestContext, pathToContract str return scAddr, owner } -// PrepareRelayerTxData - -func PrepareRelayerTxData(innerTx *transaction.Transaction) []byte { - userTxBytes, _ := protoMarshalizer.Marshal(innerTx) - return []byte(core.RelayedTransaction + "@" + hex.EncodeToString(userTxBytes)) -} - // CheckOwnerAddr - func CheckOwnerAddr(t *testing.T, testContext *vm.VMTestContext, scAddr []byte, owner []byte) { acc, err := testContext.Accounts.GetExistingAccount(scAddr) diff --git a/process/transaction/export_test.go b/process/transaction/export_test.go index 742fc618b6d..e98a9faa4f3 100644 --- a/process/transaction/export_test.go +++ b/process/transaction/export_test.go @@ -79,3 +79,8 @@ func (txProc *txProcessor) ExecuteFailedRelayedTransaction( originalTxHash, errorMsg) } + +// ShouldIncreaseNonce - +func (txProc *txProcessor) ShouldIncreaseNonce(executionErr error) bool { + return txProc.shouldIncreaseNonce(executionErr) +} diff --git a/process/transaction/shardProcess.go b/process/transaction/shardProcess.go index 06d20701ed4..9c3472a94cb 100644 --- a/process/transaction/shardProcess.go +++ b/process/transaction/shardProcess.go @@ -672,6 +672,7 @@ func (txProc *txProcessor) computeRelayedTxFees(tx *transaction.Transaction) rel func (txProc *txProcessor) removeValueAndConsumedFeeFromUser( userTx *transaction.Transaction, relayedTxValue *big.Int, + executionErr error, ) error { userAcnt, err := txProc.getAccountFromAddress(userTx.SndAddr) if err != nil { @@ -690,7 +691,10 @@ func (txProc *txProcessor) removeValueAndConsumedFeeFromUser( if err != nil { return err } - userAcnt.IncreaseNonce(1) + + if txProc.shouldIncreaseNonce(executionErr) { + userAcnt.IncreaseNonce(1) + } err = txProc.accounts.SaveAccount(userAcnt) if err != nil { @@ -735,7 +739,7 @@ func (txProc *txProcessor) processUserTx( txType, dstShardTxType := txProc.txTypeHandler.ComputeTransactionType(userTx) err = txProc.checkTxValues(userTx, acntSnd, acntDst, true) if err != nil { - errRemove := txProc.removeValueAndConsumedFeeFromUser(userTx, relayedTxValue) + errRemove := txProc.removeValueAndConsumedFeeFromUser(userTx, relayedTxValue, err) if errRemove != nil { return vmcommon.UserError, errRemove } @@ -787,7 +791,7 @@ func (txProc *txProcessor) processUserTx( returnCode, err = txProc.scProcessor.ExecuteBuiltInFunction(scrFromTx, acntSnd, acntDst) default: err = process.ErrWrongTransaction - errRemove := txProc.removeValueAndConsumedFeeFromUser(userTx, relayedTxValue) + errRemove := txProc.removeValueAndConsumedFeeFromUser(userTx, relayedTxValue, err) if errRemove != nil { return vmcommon.UserError, errRemove } @@ -940,6 +944,19 @@ func (txProc *txProcessor) executeFailedRelayedUserTx( return nil } +func (txProc *txProcessor) shouldIncreaseNonce(executionErr error) bool { + if !txProc.enableEpochsHandler.IsRelayedNonceFixEnabled() { + return true + } + + // todo add not executable for guardians + if errors.Is(executionErr, process.ErrLowerNonceInTransaction) || errors.Is(executionErr, process.ErrHigherNonceInTransaction) { + return false + } + + return true +} + // IsInterfaceNil returns true if there is no value under the interface func (txProc *txProcessor) IsInterfaceNil() bool { return txProc == nil diff --git a/process/transaction/shardProcess_test.go b/process/transaction/shardProcess_test.go index 8a881499a4b..c7492965e91 100644 --- a/process/transaction/shardProcess_test.go +++ b/process/transaction/shardProcess_test.go @@ -5,6 +5,7 @@ import ( "crypto/rand" "encoding/hex" "errors" + "fmt" "math/big" "testing" @@ -3236,3 +3237,37 @@ func TestTxProcessor_ExecuteFailingRelayedTxShouldNotHaveNegativeFee(t *testing. assert.Nil(t, err) assert.False(t, negativeCost) } + +func TestTxProcessor_shouldIncreaseNonce(t *testing.T) { + t.Parallel() + + t.Run("fix not enabled, should return true", func(t *testing.T) { + args := createArgsForTxProcessor() + args.EnableEpochsHandler = &testscommon.EnableEpochsHandlerStub{ + IsRelayedNonceFixEnabledField: false, + } + txProc, _ := txproc.NewTxProcessor(args) + + assert.True(t, txProc.ShouldIncreaseNonce(nil)) + }) + t.Run("fix enabled, different errors should return true", func(t *testing.T) { + args := createArgsForTxProcessor() + args.EnableEpochsHandler = &testscommon.EnableEpochsHandlerStub{ + IsRelayedNonceFixEnabledField: true, + } + txProc, _ := txproc.NewTxProcessor(args) + + assert.True(t, txProc.ShouldIncreaseNonce(nil)) + assert.True(t, txProc.ShouldIncreaseNonce(fmt.Errorf("random error"))) + }) + t.Run("fix enabled, errors for an un-executable transaction should return false", func(t *testing.T) { + args := createArgsForTxProcessor() + args.EnableEpochsHandler = &testscommon.EnableEpochsHandlerStub{ + IsRelayedNonceFixEnabledField: true, + } + txProc, _ := txproc.NewTxProcessor(args) + + assert.False(t, txProc.ShouldIncreaseNonce(process.ErrLowerNonceInTransaction)) + assert.False(t, txProc.ShouldIncreaseNonce(process.ErrHigherNonceInTransaction)) + }) +} diff --git a/sharding/mock/enableEpochsHandlerMock.go b/sharding/mock/enableEpochsHandlerMock.go index 6173c091e32..c57f7e899e8 100644 --- a/sharding/mock/enableEpochsHandlerMock.go +++ b/sharding/mock/enableEpochsHandlerMock.go @@ -566,6 +566,11 @@ func (mock *EnableEpochsHandlerMock) IsAlwaysSaveTokenMetaDataEnabled() bool { return false } +// IsRelayedNonceFixEnabled - +func (mock *EnableEpochsHandlerMock) IsRelayedNonceFixEnabled() bool { + return false +} + // IsInterfaceNil returns true if there is no value under the interface func (mock *EnableEpochsHandlerMock) IsInterfaceNil() bool { return mock == nil diff --git a/testscommon/enableEpochsHandlerStub.go b/testscommon/enableEpochsHandlerStub.go index 092131f8ebc..afdbe189320 100644 --- a/testscommon/enableEpochsHandlerStub.go +++ b/testscommon/enableEpochsHandlerStub.go @@ -117,6 +117,7 @@ type EnableEpochsHandlerStub struct { IsMaxBlockchainHookCountersFlagEnabledField bool IsWipeSingleNFTLiquidityDecreaseEnabledField bool IsAlwaysSaveTokenMetaDataEnabledField bool + IsRelayedNonceFixEnabledField bool } // ResetPenalizedTooMuchGasFlag - @@ -1014,6 +1015,14 @@ func (stub *EnableEpochsHandlerStub) IsAlwaysSaveTokenMetaDataEnabled() bool { return stub.IsAlwaysSaveTokenMetaDataEnabledField } +// IsRelayedNonceFixEnabled - +func (stub *EnableEpochsHandlerStub) IsRelayedNonceFixEnabled() bool { + stub.RLock() + defer stub.RUnlock() + + return stub.IsRelayedNonceFixEnabledField +} + // IsInterfaceNil - func (stub *EnableEpochsHandlerStub) IsInterfaceNil() bool { return stub == nil