From 57301ae3f6e49f1dbc7aedbce879f5e275468f48 Mon Sep 17 00:00:00 2001 From: Sorin Stanculeanu Date: Tue, 29 Oct 2024 16:01:40 +0200 Subject: [PATCH 01/39] new relayed v3 --- api/groups/transactionGroup.go | 32 +- cmd/node/config/enableEpochs.toml | 3 + common/constants.go | 4 + common/enablers/enableEpochsHandler.go | 6 + common/enablers/enableEpochsHandler_test.go | 2 + config/epochConfig.go | 1 + config/tomlConfig_test.go | 4 + go.mod | 2 +- go.sum | 4 +- .../relayedTx/relayedTx_test.go | 548 +++++++++++++++++- .../multiShard/relayedTx/common.go | 57 ++ .../multiShard/relayedTx/relayedTx_test.go | 6 + integrationTests/testProcessorNode.go | 37 +- node/external/dtos.go | 32 +- .../transactionAPI/apiTransactionProcessor.go | 13 +- node/external/transactionAPI/fieldsHandler.go | 2 + .../transactionAPI/gasUsedAndFeeProcessor.go | 29 +- node/external/transactionAPI/unmarshaller.go | 18 +- node/metrics/metrics.go | 1 + node/metrics/metrics_test.go | 2 + node/node.go | 20 + .../transactionsFeeProcessor.go | 10 +- process/constants.go | 4 + process/coordinator/transactionType.go | 10 + process/coordinator/transactionType_test.go | 28 + process/dataValidators/txValidator.go | 38 +- process/dataValidators/txValidator_test.go | 114 +++- process/economics/economicsData.go | 15 +- process/errors.go | 6 + process/transaction/baseProcess.go | 27 +- process/transaction/export_test.go | 2 + process/transaction/interceptedTransaction.go | 60 +- .../interceptedTransaction_test.go | 41 ++ process/transaction/shardProcess.go | 78 ++- process/transaction/shardProcess_test.go | 165 +++++- statusHandler/statusMetricsProvider.go | 1 + statusHandler/statusMetricsProvider_test.go | 2 + 37 files changed, 1299 insertions(+), 125 deletions(-) diff --git a/api/groups/transactionGroup.go b/api/groups/transactionGroup.go index f35969ed701..86f04dfca22 100644 --- a/api/groups/transactionGroup.go +++ b/api/groups/transactionGroup.go @@ -720,21 +720,23 @@ func (tg *transactionGroup) getTransactionsPoolNonceGapsForSender(sender string, func (tg *transactionGroup) createTransaction(receivedTx *transaction.FrontendTransaction) (*transaction.Transaction, []byte, error) { txArgs := &external.ArgsCreateTransaction{ - Nonce: receivedTx.Nonce, - Value: receivedTx.Value, - Receiver: receivedTx.Receiver, - ReceiverUsername: receivedTx.ReceiverUsername, - Sender: receivedTx.Sender, - SenderUsername: receivedTx.SenderUsername, - GasPrice: receivedTx.GasPrice, - GasLimit: receivedTx.GasLimit, - DataField: receivedTx.Data, - SignatureHex: receivedTx.Signature, - ChainID: receivedTx.ChainID, - Version: receivedTx.Version, - Options: receivedTx.Options, - Guardian: receivedTx.GuardianAddr, - GuardianSigHex: receivedTx.GuardianSignature, + Nonce: receivedTx.Nonce, + Value: receivedTx.Value, + Receiver: receivedTx.Receiver, + ReceiverUsername: receivedTx.ReceiverUsername, + Sender: receivedTx.Sender, + SenderUsername: receivedTx.SenderUsername, + GasPrice: receivedTx.GasPrice, + GasLimit: receivedTx.GasLimit, + DataField: receivedTx.Data, + SignatureHex: receivedTx.Signature, + ChainID: receivedTx.ChainID, + Version: receivedTx.Version, + Options: receivedTx.Options, + Guardian: receivedTx.GuardianAddr, + GuardianSigHex: receivedTx.GuardianSignature, + Relayer: receivedTx.RelayerAddr, + RelayerSignatureHex: receivedTx.RelayerSignature, } start := time.Now() tx, txHash, err := tg.getFacade().CreateTransaction(txArgs) diff --git a/cmd/node/config/enableEpochs.toml b/cmd/node/config/enableEpochs.toml index 9ca2083a352..0b45c059ef1 100644 --- a/cmd/node/config/enableEpochs.toml +++ b/cmd/node/config/enableEpochs.toml @@ -333,6 +333,9 @@ # FixRelayedMoveBalanceToNonPayableSCEnableEpoch represents the epoch when the fix for relayed move balance to non payable sc will be enabled FixRelayedMoveBalanceToNonPayableSCEnableEpoch = 4 + # RelayedTransactionsV3 represents the epoch when the relayed transactions v3 will be enabled + RelayedTransactionsV3 = 5 + # BLSMultiSignerEnableEpoch represents the activation epoch for different types of BLS multi-signers BLSMultiSignerEnableEpoch = [ { EnableEpoch = 0, Type = "no-KOSK" }, diff --git a/common/constants.go b/common/constants.go index 2b56d2f388b..67725bdb0b3 100644 --- a/common/constants.go +++ b/common/constants.go @@ -737,6 +737,9 @@ const ( // MetricFixRelayedMoveBalanceToNonPayableSCEnableEpoch represents the epoch when the fix for relayed move balance to non-payable sc is enabled MetricFixRelayedMoveBalanceToNonPayableSCEnableEpoch = "erd_fix_relayed_move_balance_to_non_payable_sc_enable_epoch" + // MetricRelayedTransactionsV3EnableEpoch represents the epoch when the relayed transactions v3 are enabled + MetricRelayedTransactionsV3EnableEpoch = "erd_relayed_transactions_v3_enable_epoch" + // MetricMaxNodesChangeEnableEpoch holds configuration for changing the maximum number of nodes and the enabling epoch MetricMaxNodesChangeEnableEpoch = "erd_max_nodes_change_enable_epoch" @@ -1234,5 +1237,6 @@ const ( FixRelayedBaseCostFlag core.EnableEpochFlag = "FixRelayedBaseCostFlag" MultiESDTNFTTransferAndExecuteByUserFlag core.EnableEpochFlag = "MultiESDTNFTTransferAndExecuteByUserFlag" FixRelayedMoveBalanceToNonPayableSCFlag core.EnableEpochFlag = "FixRelayedMoveBalanceToNonPayableSCFlag" + RelayedTransactionsV3Flag core.EnableEpochFlag = "RelayedTransactionsV3Flag" // all new flags must be added to createAllFlagsMap method, as part of enableEpochsHandler allFlagsDefined ) diff --git a/common/enablers/enableEpochsHandler.go b/common/enablers/enableEpochsHandler.go index 65cc7762796..cf31b3ab310 100644 --- a/common/enablers/enableEpochsHandler.go +++ b/common/enablers/enableEpochsHandler.go @@ -780,6 +780,12 @@ func (handler *enableEpochsHandler) createAllFlagsMap() { }, activationEpoch: handler.enableEpochsConfig.FixRelayedMoveBalanceToNonPayableSCEnableEpoch, }, + common.RelayedTransactionsV3Flag: { + isActiveInEpoch: func(epoch uint32) bool { + return epoch >= handler.enableEpochsConfig.RelayedTransactionsV3EnableEpoch + }, + activationEpoch: handler.enableEpochsConfig.RelayedTransactionsV3EnableEpoch, + }, } } diff --git a/common/enablers/enableEpochsHandler_test.go b/common/enablers/enableEpochsHandler_test.go index d0f9191055e..a5fce844f1b 100644 --- a/common/enablers/enableEpochsHandler_test.go +++ b/common/enablers/enableEpochsHandler_test.go @@ -123,6 +123,7 @@ func createEnableEpochsConfig() config.EnableEpochs { MultiESDTNFTTransferAndExecuteByUserEnableEpoch: 106, FixRelayedMoveBalanceToNonPayableSCEnableEpoch: 107, UseGasBoundedShouldFailExecutionEnableEpoch: 108, + RelayedTransactionsV3EnableEpoch: 109, } } @@ -448,6 +449,7 @@ func TestEnableEpochsHandler_GetActivationEpoch(t *testing.T) { require.Equal(t, cfg.FixRelayedBaseCostEnableEpoch, handler.GetActivationEpoch(common.FixRelayedBaseCostFlag)) require.Equal(t, cfg.MultiESDTNFTTransferAndExecuteByUserEnableEpoch, handler.GetActivationEpoch(common.MultiESDTNFTTransferAndExecuteByUserFlag)) require.Equal(t, cfg.FixRelayedMoveBalanceToNonPayableSCEnableEpoch, handler.GetActivationEpoch(common.FixRelayedMoveBalanceToNonPayableSCFlag)) + require.Equal(t, cfg.RelayedTransactionsV3EnableEpoch, handler.GetActivationEpoch(common.RelayedTransactionsV3Flag)) } func TestEnableEpochsHandler_IsInterfaceNil(t *testing.T) { diff --git a/config/epochConfig.go b/config/epochConfig.go index a7fd67c680a..9a76744dbf4 100644 --- a/config/epochConfig.go +++ b/config/epochConfig.go @@ -122,6 +122,7 @@ type EnableEpochs struct { FixRelayedBaseCostEnableEpoch uint32 MultiESDTNFTTransferAndExecuteByUserEnableEpoch uint32 FixRelayedMoveBalanceToNonPayableSCEnableEpoch uint32 + RelayedTransactionsV3EnableEpoch uint32 BLSMultiSignerEnableEpoch []MultiSignerConfig } diff --git a/config/tomlConfig_test.go b/config/tomlConfig_test.go index 39a582b1ef2..767e37e3950 100644 --- a/config/tomlConfig_test.go +++ b/config/tomlConfig_test.go @@ -884,6 +884,9 @@ func TestEnableEpochConfig(t *testing.T) { # FixRelayedMoveBalanceToNonPayableSCEnableEpoch represents the epoch when the fix for relayed move balance to non payable sc will be enabled FixRelayedMoveBalanceToNonPayableSCEnableEpoch = 102 + # RelayedTransactionsV3EnableEpoch represents the epoch when the relayed transactions v3 will be enabled + RelayedTransactionsV3EnableEpoch = 103 + # MaxNodesChangeEnableEpoch holds configuration for changing the maximum number of nodes and the enabling epoch MaxNodesChangeEnableEpoch = [ { EpochEnable = 44, MaxNumNodes = 2169, NodesToShufflePerShard = 80 }, @@ -1004,6 +1007,7 @@ func TestEnableEpochConfig(t *testing.T) { FixRelayedBaseCostEnableEpoch: 100, MultiESDTNFTTransferAndExecuteByUserEnableEpoch: 101, FixRelayedMoveBalanceToNonPayableSCEnableEpoch: 102, + RelayedTransactionsV3EnableEpoch: 103, MaxNodesChangeEnableEpoch: []MaxNodesChangeConfig{ { EpochEnable: 44, diff --git a/go.mod b/go.mod index b1eb1aa4ddb..36b4f177b4b 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( github.com/klauspost/cpuid/v2 v2.2.5 github.com/mitchellh/mapstructure v1.5.0 github.com/multiversx/mx-chain-communication-go v1.1.0 - github.com/multiversx/mx-chain-core-go v1.2.23-0.20241018134424-75bab2a9058c + github.com/multiversx/mx-chain-core-go v1.2.23-0.20241024081246-bbc08e634e51 github.com/multiversx/mx-chain-crypto-go v1.2.12 github.com/multiversx/mx-chain-es-indexer-go v1.7.10-0.20241018130218-f48c7282690b github.com/multiversx/mx-chain-logger-go v1.0.15 diff --git a/go.sum b/go.sum index 32ad88ef7af..a6dfb332f3c 100644 --- a/go.sum +++ b/go.sum @@ -387,8 +387,8 @@ github.com/multiversx/concurrent-map v0.1.4 h1:hdnbM8VE4b0KYJaGY5yJS2aNIW9TFFsUY github.com/multiversx/concurrent-map v0.1.4/go.mod h1:8cWFRJDOrWHOTNSqgYCUvwT7c7eFQ4U2vKMOp4A/9+o= github.com/multiversx/mx-chain-communication-go v1.1.0 h1:J7bX6HoN3HiHY7cUeEjG8AJWgQDDPcY+OPDOsSUOkRE= github.com/multiversx/mx-chain-communication-go v1.1.0/go.mod h1:WK6bP4pGEHGDDna/AYRIMtl6G9OA0NByI1Lw8PmOnRM= -github.com/multiversx/mx-chain-core-go v1.2.23-0.20241018134424-75bab2a9058c h1:hPCfMSj2vd9xNkARNxB1b3b9k8taFb+Xfja+WK97jno= -github.com/multiversx/mx-chain-core-go v1.2.23-0.20241018134424-75bab2a9058c/go.mod h1:B5zU4MFyJezmEzCsAHE9YNULmGCm2zbPHvl9hazNxmE= +github.com/multiversx/mx-chain-core-go v1.2.23-0.20241024081246-bbc08e634e51 h1:oQPa+bF311LKiCKWrWcDb8cxNAnz/EcuRIV45rCj6Rg= +github.com/multiversx/mx-chain-core-go v1.2.23-0.20241024081246-bbc08e634e51/go.mod h1:B5zU4MFyJezmEzCsAHE9YNULmGCm2zbPHvl9hazNxmE= github.com/multiversx/mx-chain-crypto-go v1.2.12 h1:zWip7rpUS4CGthJxfKn5MZfMfYPjVjIiCID6uX5BSOk= github.com/multiversx/mx-chain-crypto-go v1.2.12/go.mod h1:HzcPpCm1zanNct/6h2rIh+MFrlXbjA5C8+uMyXj3LI4= github.com/multiversx/mx-chain-es-indexer-go v1.7.10-0.20241018130218-f48c7282690b h1:GYvm0yGkdQ3OCfNqnyIQNzAzydN3cES8noJZ3eZHN1A= diff --git a/integrationTests/chainSimulator/relayedTx/relayedTx_test.go b/integrationTests/chainSimulator/relayedTx/relayedTx_test.go index f7c0f74649b..7021133fddd 100644 --- a/integrationTests/chainSimulator/relayedTx/relayedTx_test.go +++ b/integrationTests/chainSimulator/relayedTx/relayedTx_test.go @@ -4,6 +4,7 @@ import ( "encoding/hex" "encoding/json" "math/big" + "strconv" "strings" "testing" "time" @@ -17,6 +18,8 @@ import ( "github.com/multiversx/mx-chain-go/node/chainSimulator/components/api" "github.com/multiversx/mx-chain-go/node/chainSimulator/configs" "github.com/multiversx/mx-chain-go/node/chainSimulator/dtos" + chainSimulatorProcess "github.com/multiversx/mx-chain-go/node/chainSimulator/process" + "github.com/multiversx/mx-chain-go/process" "github.com/stretchr/testify/require" ) @@ -25,8 +28,10 @@ const ( minGasPrice = 1_000_000_000 minGasLimit = 50_000 gasPerDataByte = 1_500 + deductionFactor = 100 txVersion = 2 - mockTxSignature = "sig" + mockTxSignature = "ssig" + mockRelayerTxSignature = "rsig" maxNumOfBlocksToGenerateWhenExecutingTx = 10 roundsPerEpoch = 30 ) @@ -35,17 +40,448 @@ var ( oneEGLD = big.NewInt(1000000000000000000) ) +func TestRelayedV3WithChainSimulator(t *testing.T) { + t.Run("successful intra shard move balance with exact gas", testRelayedV3MoveBalance(0, 0, false)) + t.Run("successful intra shard move balance with extra gas", testRelayedV3MoveBalance(0, 0, true)) + t.Run("successful cross shard move balance with exact gas", testRelayedV3MoveBalance(0, 1, false)) + t.Run("successful cross shard move balance with extra gas", testRelayedV3MoveBalance(0, 1, true)) + t.Run("intra shard move balance, lower nonce", testRelayedV3MoveBalanceLowerNonce(0, 0)) + t.Run("cross shard move balance, lower nonce", testRelayedV3MoveBalanceLowerNonce(0, 1)) + t.Run("intra shard move balance, invalid gas", testRelayedV3MoveInvalidGasLimit(0, 0)) + t.Run("cross shard move balance, invalid gas", testRelayedV3MoveInvalidGasLimit(0, 1)) + + t.Run("successful intra shard sc call with refunds", testRelayedV3ScCall(0, 0)) + t.Run("successful cross shard sc call with refunds", testRelayedV3ScCall(0, 1)) + t.Run("intra shard sc call, invalid gas", testRelayedV3ScCallInvalidGasLimit(0, 0)) + t.Run("cross shard sc call, invalid gas", testRelayedV3ScCallInvalidGasLimit(0, 1)) + t.Run("intra shard sc call, invalid method", testRelayedV3ScCallInvalidMethod(0, 0)) + t.Run("cross shard sc call, invalid method", testRelayedV3ScCallInvalidMethod(0, 1)) +} + +func testRelayedV3MoveBalance( + relayerShard uint32, + destinationShard uint32, + extraGas bool, +) func(t *testing.T) { + return func(t *testing.T) { + if testing.Short() { + t.Skip("this is not a short test") + } + + providedActivationEpoch := uint32(1) + alterConfigsFunc := func(cfg *config.Configs) { + cfg.EpochConfig.EnableEpochs.FixRelayedBaseCostEnableEpoch = providedActivationEpoch + cfg.EpochConfig.EnableEpochs.RelayedTransactionsV3EnableEpoch = providedActivationEpoch + } + + cs := startChainSimulator(t, alterConfigsFunc) + defer cs.Close() + + initialBalance := big.NewInt(0).Mul(oneEGLD, big.NewInt(10)) + relayer, err := cs.GenerateAndMintWalletAddress(relayerShard, initialBalance) + require.NoError(t, err) + + sender, err := cs.GenerateAndMintWalletAddress(relayerShard, initialBalance) + require.NoError(t, err) + + receiver, err := cs.GenerateAndMintWalletAddress(destinationShard, big.NewInt(0)) + require.NoError(t, err) + + // generate one block so the minting has effect + err = cs.GenerateBlocks(1) + require.NoError(t, err) + + gasLimit := minGasLimit * 2 + extraGasLimit := 0 + if extraGas { + extraGasLimit = minGasLimit + } + relayedTx := generateRelayedV3Transaction(sender.Bytes, 0, receiver.Bytes, relayer.Bytes, oneEGLD, "", uint64(gasLimit+extraGasLimit)) + + result, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(relayedTx, maxNumOfBlocksToGenerateWhenExecutingTx) + require.NoError(t, err) + + // check fee fields + initiallyPaidFee, fee, gasUsed := computeTxGasAndFeeBasedOnRefund(result, big.NewInt(0), true) + require.Equal(t, initiallyPaidFee.String(), result.InitiallyPaidFee) + require.Equal(t, fee.String(), result.Fee) + require.Equal(t, gasUsed, result.GasUsed) + + // check relayer balance + relayerBalanceAfter := getBalance(t, cs, relayer) + relayerFee := big.NewInt(0).Sub(initialBalance, relayerBalanceAfter) + require.Equal(t, fee.String(), relayerFee.String()) + + // check sender balance + senderBalanceAfter := getBalance(t, cs, sender) + senderBalanceDiff := big.NewInt(0).Sub(initialBalance, senderBalanceAfter) + require.Equal(t, oneEGLD.String(), senderBalanceDiff.String()) + + // check receiver balance + receiverBalanceAfter := getBalance(t, cs, receiver) + require.Equal(t, oneEGLD.String(), receiverBalanceAfter.String()) + + // check scr + require.Equal(t, 1, len(result.SmartContractResults)) + require.Equal(t, relayer.Bech32, result.SmartContractResults[0].RelayerAddr) + require.Equal(t, sender.Bech32, result.SmartContractResults[0].SndAddr) + require.Equal(t, receiver.Bech32, result.SmartContractResults[0].RcvAddr) + require.Equal(t, relayedTx.Value, result.SmartContractResults[0].Value) + + // check intra shard logs, should be none + require.Nil(t, result.Logs) + + // check cross shard log, should be one completedTxEvent + if relayerShard == destinationShard { + return + } + scrResult, err := cs.GetNodeHandler(destinationShard).GetFacadeHandler().GetTransaction(result.SmartContractResults[0].Hash, true) + require.NoError(t, err) + require.NotNil(t, scrResult.Logs) + require.Equal(t, 1, len(scrResult.Logs.Events)) + require.Contains(t, scrResult.Logs.Events[0].Identifier, core.CompletedTxEventIdentifier) + } +} + +func testRelayedV3MoveBalanceLowerNonce( + relayerShard uint32, + receiverShard uint32, +) func(t *testing.T) { + return func(t *testing.T) { + if testing.Short() { + t.Skip("this is not a short test") + } + + providedActivationEpoch := uint32(1) + alterConfigsFunc := func(cfg *config.Configs) { + cfg.EpochConfig.EnableEpochs.FixRelayedBaseCostEnableEpoch = providedActivationEpoch + cfg.EpochConfig.EnableEpochs.RelayedTransactionsV3EnableEpoch = providedActivationEpoch + } + + cs := startChainSimulator(t, alterConfigsFunc) + defer cs.Close() + + initialBalance := big.NewInt(0).Mul(oneEGLD, big.NewInt(10)) + relayer, err := cs.GenerateAndMintWalletAddress(relayerShard, initialBalance) + require.NoError(t, err) + + sender, err := cs.GenerateAndMintWalletAddress(relayerShard, initialBalance) + require.NoError(t, err) + + receiver, err := cs.GenerateAndMintWalletAddress(receiverShard, big.NewInt(0)) + require.NoError(t, err) + + // generate one block so the minting has effect + err = cs.GenerateBlocks(1) + require.NoError(t, err) + + gasLimit := minGasLimit * 2 + relayedTx := generateRelayedV3Transaction(sender.Bytes, 0, receiver.Bytes, relayer.Bytes, oneEGLD, "", uint64(gasLimit)) + + _, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(relayedTx, maxNumOfBlocksToGenerateWhenExecutingTx) + require.NoError(t, err) + + // send same tx again, lower nonce + result, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(relayedTx, maxNumOfBlocksToGenerateWhenExecutingTx) + require.Contains(t, err.Error(), process.ErrWrongTransaction.Error()) + require.Nil(t, result) + } +} + +func testRelayedV3MoveInvalidGasLimit( + relayerShard uint32, + receiverShard uint32, +) func(t *testing.T) { + return func(t *testing.T) { + if testing.Short() { + t.Skip("this is not a short test") + } + + providedActivationEpoch := uint32(1) + alterConfigsFunc := func(cfg *config.Configs) { + cfg.EpochConfig.EnableEpochs.FixRelayedBaseCostEnableEpoch = providedActivationEpoch + cfg.EpochConfig.EnableEpochs.RelayedTransactionsV3EnableEpoch = providedActivationEpoch + } + + cs := startChainSimulator(t, alterConfigsFunc) + defer cs.Close() + + initialBalance := big.NewInt(0).Mul(oneEGLD, big.NewInt(10)) + relayer, err := cs.GenerateAndMintWalletAddress(relayerShard, initialBalance) + require.NoError(t, err) + + sender, err := cs.GenerateAndMintWalletAddress(relayerShard, initialBalance) + require.NoError(t, err) + + receiver, err := cs.GenerateAndMintWalletAddress(receiverShard, big.NewInt(0)) + require.NoError(t, err) + + // generate one block so the minting has effect + err = cs.GenerateBlocks(1) + require.NoError(t, err) + + gasLimit := minGasLimit + relayedTx := generateRelayedV3Transaction(sender.Bytes, 0, receiver.Bytes, relayer.Bytes, oneEGLD, "", uint64(gasLimit)) + + result, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(relayedTx, maxNumOfBlocksToGenerateWhenExecutingTx) + require.Contains(t, err.Error(), process.ErrInsufficientGasLimitInTx.Error()) + require.Nil(t, result) + } +} + +func testRelayedV3ScCall( + relayerShard uint32, + ownerShard uint32, +) func(t *testing.T) { + return func(t *testing.T) { + if testing.Short() { + t.Skip("this is not a short test") + } + + providedActivationEpoch := uint32(1) + alterConfigsFunc := func(cfg *config.Configs) { + cfg.EpochConfig.EnableEpochs.FixRelayedBaseCostEnableEpoch = providedActivationEpoch + cfg.EpochConfig.EnableEpochs.RelayedTransactionsV3EnableEpoch = providedActivationEpoch + } + + cs := startChainSimulator(t, alterConfigsFunc) + defer cs.Close() + + initialBalance := big.NewInt(0).Mul(oneEGLD, big.NewInt(10)) + relayer, err := cs.GenerateAndMintWalletAddress(relayerShard, initialBalance) + require.NoError(t, err) + + sender, err := cs.GenerateAndMintWalletAddress(relayerShard, initialBalance) + require.NoError(t, err) + + owner, err := cs.GenerateAndMintWalletAddress(ownerShard, initialBalance) + require.NoError(t, err) + + // generate one block so the minting has effect + err = cs.GenerateBlocks(1) + require.NoError(t, err) + + resultDeploy, scAddressBytes := deployAdder(t, cs, owner, 0) + refundDeploy := getRefundValue(resultDeploy.SmartContractResults) + + // send relayed tx + txDataAdd := "add@" + hex.EncodeToString(big.NewInt(1).Bytes()) + gasLimit := uint64(3000000) + relayedTx := generateRelayedV3Transaction(sender.Bytes, 0, scAddressBytes, relayer.Bytes, big.NewInt(0), txDataAdd, gasLimit) + + result, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(relayedTx, maxNumOfBlocksToGenerateWhenExecutingTx) + require.NoError(t, err) + + // if cross shard, generate few more blocks for eventual refunds to be executed + if relayerShard != ownerShard { + require.NoError(t, cs.GenerateBlocks(maxNumOfBlocksToGenerateWhenExecutingTx)) + } + + checkSum(t, cs.GetNodeHandler(ownerShard), scAddressBytes, owner.Bytes, 1) + + refundValue := getRefundValue(result.SmartContractResults) + require.NotZero(t, refundValue.Uint64()) + + // check fee fields + initiallyPaidFee, fee, gasUsed := computeTxGasAndFeeBasedOnRefund(result, refundValue, false) + require.Equal(t, initiallyPaidFee.String(), result.InitiallyPaidFee) + require.Equal(t, fee.String(), result.Fee) + require.Equal(t, gasUsed, result.GasUsed) + + // check relayer balance + relayerBalanceAfter := getBalance(t, cs, relayer) + relayerFee := big.NewInt(0).Sub(initialBalance, relayerBalanceAfter) + require.Equal(t, fee.String(), relayerFee.String()) + + // check sender balance + senderBalanceAfter := getBalance(t, cs, sender) + require.Equal(t, initialBalance.String(), senderBalanceAfter.String()) + + // check owner balance + _, feeDeploy, _ := computeTxGasAndFeeBasedOnRefund(resultDeploy, refundDeploy, false) + ownerBalanceAfter := getBalance(t, cs, owner) + ownerFee := big.NewInt(0).Sub(initialBalance, ownerBalanceAfter) + require.Equal(t, feeDeploy.String(), ownerFee.String()) + + // check scrs + require.Equal(t, 2, len(result.SmartContractResults)) + for _, scr := range result.SmartContractResults { + checkSCRSucceeded(t, cs, scr) + } + } +} + +func testRelayedV3ScCallInvalidGasLimit( + relayerShard uint32, + ownerShard uint32, +) func(t *testing.T) { + return func(t *testing.T) { + if testing.Short() { + t.Skip("this is not a short test") + } + + providedActivationEpoch := uint32(1) + alterConfigsFunc := func(cfg *config.Configs) { + cfg.EpochConfig.EnableEpochs.FixRelayedBaseCostEnableEpoch = providedActivationEpoch + cfg.EpochConfig.EnableEpochs.RelayedTransactionsV3EnableEpoch = providedActivationEpoch + } + + cs := startChainSimulator(t, alterConfigsFunc) + defer cs.Close() + + initialBalance := big.NewInt(0).Mul(oneEGLD, big.NewInt(10)) + relayer, err := cs.GenerateAndMintWalletAddress(relayerShard, initialBalance) + require.NoError(t, err) + + sender, err := cs.GenerateAndMintWalletAddress(relayerShard, initialBalance) + require.NoError(t, err) + + owner, err := cs.GenerateAndMintWalletAddress(ownerShard, initialBalance) + require.NoError(t, err) + + // generate one block so the minting has effect + err = cs.GenerateBlocks(1) + require.NoError(t, err) + + _, scAddressBytes := deployAdder(t, cs, owner, 0) + + // send relayed tx with less gas limit + txDataAdd := "add@" + hex.EncodeToString(big.NewInt(1).Bytes()) + gasLimit := gasPerDataByte*len(txDataAdd) + minGasLimit + minGasLimit + relayedTx := generateRelayedV3Transaction(sender.Bytes, 0, scAddressBytes, relayer.Bytes, big.NewInt(0), txDataAdd, uint64(gasLimit)) + + result, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(relayedTx, maxNumOfBlocksToGenerateWhenExecutingTx) + require.NoError(t, err) + + logs := result.Logs + // if cross shard, generate few more blocks for cross shard scrs + if relayerShard != ownerShard { + require.NoError(t, cs.GenerateBlocks(maxNumOfBlocksToGenerateWhenExecutingTx)) + logs = result.SmartContractResults[0].Logs + } + + require.NotNil(t, logs) + require.Equal(t, 2, len(logs.Events)) + for _, event := range logs.Events { + if event.Identifier == core.SignalErrorOperation { + continue + } + + require.Equal(t, 1, len(event.AdditionalData)) + require.Contains(t, string(event.AdditionalData[0]), "[not enough gas]") + } + + refundValue := getRefundValue(result.SmartContractResults) + require.Zero(t, refundValue.Uint64()) + + // check fee fields, should consume full gas + initiallyPaidFee, fee, gasUsed := computeTxGasAndFeeBasedOnRefund(result, refundValue, false) + require.Equal(t, initiallyPaidFee.String(), result.InitiallyPaidFee) + require.Equal(t, fee.String(), result.Fee) + require.Equal(t, result.InitiallyPaidFee, result.Fee) + require.Equal(t, gasUsed, result.GasUsed) + require.Equal(t, relayedTx.GasLimit, result.GasUsed) + + // check relayer balance + relayerBalanceAfter := getBalance(t, cs, relayer) + relayerFee := big.NewInt(0).Sub(initialBalance, relayerBalanceAfter) + require.Equal(t, fee.String(), relayerFee.String()) + + // check sender balance + senderBalanceAfter := getBalance(t, cs, sender) + require.Equal(t, initialBalance.String(), senderBalanceAfter.String()) + } +} + +func testRelayedV3ScCallInvalidMethod( + relayerShard uint32, + ownerShard uint32, +) func(t *testing.T) { + return func(t *testing.T) { + if testing.Short() { + t.Skip("this is not a short test") + } + + providedActivationEpoch := uint32(1) + alterConfigsFunc := func(cfg *config.Configs) { + cfg.EpochConfig.EnableEpochs.FixRelayedBaseCostEnableEpoch = providedActivationEpoch + cfg.EpochConfig.EnableEpochs.RelayedTransactionsV3EnableEpoch = providedActivationEpoch + } + + cs := startChainSimulator(t, alterConfigsFunc) + defer cs.Close() + + initialBalance := big.NewInt(0).Mul(oneEGLD, big.NewInt(10)) + relayer, err := cs.GenerateAndMintWalletAddress(relayerShard, initialBalance) + require.NoError(t, err) + + sender, err := cs.GenerateAndMintWalletAddress(relayerShard, initialBalance) + require.NoError(t, err) + + owner, err := cs.GenerateAndMintWalletAddress(ownerShard, initialBalance) + require.NoError(t, err) + + // generate one block so the minting has effect + err = cs.GenerateBlocks(1) + require.NoError(t, err) + + _, scAddressBytes := deployAdder(t, cs, owner, 0) + + // send relayed tx with invalid value + txDataAdd := "invalid" + gasLimit := uint64(3000000) + relayedTx := generateRelayedV3Transaction(sender.Bytes, 0, scAddressBytes, relayer.Bytes, big.NewInt(0), txDataAdd, uint64(gasLimit)) + + result, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(relayedTx, maxNumOfBlocksToGenerateWhenExecutingTx) + require.NoError(t, err) + + logs := result.Logs + // if cross shard, generate few more blocks for cross shard scrs + if relayerShard != ownerShard { + require.NoError(t, cs.GenerateBlocks(maxNumOfBlocksToGenerateWhenExecutingTx)) + logs = result.SmartContractResults[0].Logs + } + + require.NotNil(t, logs) + require.Equal(t, 2, len(logs.Events)) + for _, event := range logs.Events { + if event.Identifier == core.SignalErrorOperation { + continue + } + + require.Equal(t, 1, len(event.AdditionalData)) + require.Contains(t, string(event.AdditionalData[0]), "[invalid function (not found)]") + } + + refundValue := getRefundValue(result.SmartContractResults) + require.Zero(t, refundValue.Uint64()) // no refund, tx failed + + // check fee fields, should consume full gas + initiallyPaidFee, fee, _ := computeTxGasAndFeeBasedOnRefund(result, refundValue, false) + require.Equal(t, initiallyPaidFee.String(), result.InitiallyPaidFee) + + // check relayer balance + relayerBalanceAfter := getBalance(t, cs, relayer) + relayerFee := big.NewInt(0).Sub(initialBalance, relayerBalanceAfter) + require.Equal(t, fee.String(), relayerFee.String()) + + // check sender balance + senderBalanceAfter := getBalance(t, cs, sender) + require.Equal(t, initialBalance.String(), senderBalanceAfter.String()) + } +} + func TestFixRelayedMoveBalanceWithChainSimulator(t *testing.T) { if testing.Short() { t.Skip("this is not a short test") } - expectedFeeScCallBefore := "815294920000000" - expectedFeeScCallAfter := "873704920000000" + expectedFeeScCallBefore := "827294920000000" + expectedFeeScCallAfter := "885704920000000" t.Run("sc call", testFixRelayedMoveBalanceWithChainSimulatorScCall(expectedFeeScCallBefore, expectedFeeScCallAfter)) - expectedFeeMoveBalanceBefore := "797500000000000" // 498 * 1500 + 50000 + 5000 - expectedFeeMoveBalanceAfter := "847000000000000" // 498 * 1500 + 50000 + 50000 + expectedFeeMoveBalanceBefore := "809500000000000" // 506 * 1500 + 50000 + 500 + expectedFeeMoveBalanceAfter := "859000000000000" // 506 * 1500 + 50000 + 50000 t.Run("move balance", testFixRelayedMoveBalanceWithChainSimulatorMoveBalance(expectedFeeMoveBalanceBefore, expectedFeeMoveBalanceAfter)) } @@ -76,7 +512,7 @@ func testFixRelayedMoveBalanceWithChainSimulatorScCall( require.NoError(t, err) ownerNonce := uint64(0) - scAddressBytes := deployAdder(t, cs, owner, ownerNonce) + _, scAddressBytes := deployAdder(t, cs, owner, ownerNonce) // fast-forward until epoch 4 err = cs.GenerateBlocksUntilEpochIsReached(int32(4)) @@ -291,6 +727,13 @@ func startChainSimulator( return cs } +func generateRelayedV3Transaction(sender []byte, nonce uint64, receiver []byte, relayer []byte, value *big.Int, data string, gasLimit uint64) *transaction.Transaction { + tx := generateTransaction(sender, nonce, receiver, value, data, gasLimit) + tx.RelayerSignature = []byte(mockRelayerTxSignature) + tx.RelayerAddr = relayer + return tx +} + func generateTransaction(sender []byte, nonce uint64, receiver []byte, value *big.Int, data string, gasLimit uint64) *transaction.Transaction { return &transaction.Transaction{ Nonce: nonce, @@ -325,7 +768,7 @@ func deployAdder( cs testsChainSimulator.ChainSimulator, owner dtos.WalletAddress, ownerNonce uint64, -) []byte { +) (*transaction.ApiTransactionResult, []byte) { pkConv := cs.GetNodeHandler(0).GetCoreComponents().AddressPubKeyConverter() err := cs.GenerateBlocks(1) @@ -342,5 +785,94 @@ func deployAdder( scAddress := result.Logs.Events[0].Address scAddressBytes, _ := pkConv.Decode(scAddress) - return scAddressBytes + return result, scAddressBytes +} + +func checkSum( + t *testing.T, + nodeHandler chainSimulatorProcess.NodeHandler, + scAddress []byte, + callerAddress []byte, + expectedSum int, +) { + scQuery := &process.SCQuery{ + ScAddress: scAddress, + FuncName: "getSum", + CallerAddr: callerAddress, + CallValue: big.NewInt(0), + } + result, _, err := nodeHandler.GetFacadeHandler().ExecuteSCQuery(scQuery) + require.Nil(t, err) + require.Equal(t, "ok", result.ReturnCode) + + sum, err := strconv.Atoi(hex.EncodeToString(result.ReturnData[0])) + require.NoError(t, err) + + require.Equal(t, expectedSum, sum) +} + +func getRefundValue(scrs []*transaction.ApiSmartContractResult) *big.Int { + for _, scr := range scrs { + if scr.IsRefund { + return scr.Value + } + } + + return big.NewInt(0) +} + +func computeTxGasAndFeeBasedOnRefund( + result *transaction.ApiTransactionResult, + refund *big.Int, + isMoveBalance bool, +) (*big.Int, *big.Int, uint64) { + deductedGasPrice := uint64(minGasPrice / deductionFactor) + + initialTx := result.Tx + gasForFullPrice := uint64(minGasLimit + gasPerDataByte*len(initialTx.GetData())) + if result.ProcessingTypeOnSource == process.RelayedTxV3.String() { + gasForFullPrice += uint64(minGasLimit) // relayer fee + } + gasForDeductedPrice := initialTx.GetGasLimit() - gasForFullPrice + + initialFee := gasForFullPrice*minGasPrice + gasForDeductedPrice*deductedGasPrice + finalFee := initialFee - refund.Uint64() + + gasRefunded := refund.Uint64() / deductedGasPrice + gasConsumed := gasForFullPrice + gasForDeductedPrice - gasRefunded + + if isMoveBalance { + return big.NewInt(0).SetUint64(initialFee), big.NewInt(0).SetUint64(gasForFullPrice * minGasPrice), gasForFullPrice + } + + return big.NewInt(0).SetUint64(initialFee), big.NewInt(0).SetUint64(finalFee), gasConsumed +} + +func checkSCRSucceeded( + t *testing.T, + cs testsChainSimulator.ChainSimulator, + scr *transaction.ApiSmartContractResult, +) { + pkConv := cs.GetNodeHandler(0).GetCoreComponents().AddressPubKeyConverter() + shardC := cs.GetNodeHandler(0).GetShardCoordinator() + addr, err := pkConv.Decode(scr.RcvAddr) + require.NoError(t, err) + + senderShard := shardC.ComputeId(addr) + tx, err := cs.GetNodeHandler(senderShard).GetFacadeHandler().GetTransaction(scr.Hash, true) + require.NoError(t, err) + require.Equal(t, transaction.TxStatusSuccess, tx.Status) + + if tx.ReturnMessage == core.GasRefundForRelayerMessage { + return + } + + require.GreaterOrEqual(t, len(tx.Logs.Events), 1) + for _, event := range tx.Logs.Events { + if event.Identifier == core.WriteLogIdentifier { + continue + } + + require.Equal(t, core.CompletedTxEventIdentifier, event.Identifier) + } } diff --git a/integrationTests/multiShard/relayedTx/common.go b/integrationTests/multiShard/relayedTx/common.go index a9098c6c668..b50874f8763 100644 --- a/integrationTests/multiShard/relayedTx/common.go +++ b/integrationTests/multiShard/relayedTx/common.go @@ -123,6 +123,28 @@ func CreateAndSendRelayedAndUserTxV2( return relayedTx, userTx } +// CreateAndSendRelayedAndUserTxV3 will create and send a relayed user transaction v3 +func CreateAndSendRelayedAndUserTxV3( + nodes []*integrationTests.TestProcessorNode, + relayer *integrationTests.TestWalletAccount, + player *integrationTests.TestWalletAccount, + rcvAddr []byte, + value *big.Int, + gasLimit uint64, + txData []byte, +) (*transaction.Transaction, *transaction.Transaction) { + txDispatcherNode := getNodeWithinSameShardAsPlayer(nodes, relayer.Address) + + relayedTx := createRelayedTxV3(txDispatcherNode.EconomicsData, relayer, player, rcvAddr, value, gasLimit, txData) + + _, err := txDispatcherNode.SendTransaction(relayedTx) + if err != nil { + fmt.Println(err.Error()) + } + + return relayedTx, relayedTx +} + func createUserTx( player *integrationTests.TestWalletAccount, rcvAddr []byte, @@ -212,6 +234,41 @@ func createRelayedTxV2( return tx } +func createRelayedTxV3( + economicsFee process.FeeHandler, + relayer *integrationTests.TestWalletAccount, + player *integrationTests.TestWalletAccount, + rcvAddr []byte, + value *big.Int, + gasLimit uint64, + txData []byte, +) *transaction.Transaction { + tx := &transaction.Transaction{ + Nonce: player.Nonce, + Value: big.NewInt(0).Set(value), + RcvAddr: rcvAddr, + SndAddr: player.Address, + GasPrice: integrationTests.MinTxGasPrice, + GasLimit: gasLimit + integrationTests.MinTxGasLimit, + Data: txData, + ChainID: integrationTests.ChainID, + Version: integrationTests.MinTransactionVersion, + RelayerAddr: relayer.Address, + } + txBuff, _ := tx.GetDataForSigning(integrationTests.TestAddressPubkeyConverter, integrationTests.TestTxSignMarshalizer, integrationTests.TestTxSignHasher) + tx.Signature, _ = player.SingleSigner.Sign(player.SkTxSign, txBuff) + tx.RelayerSignature, _ = relayer.SingleSigner.Sign(relayer.SkTxSign, txBuff) + + player.Nonce++ + player.Balance.Sub(player.Balance, value) + + relayer.Nonce++ + txFee := economicsFee.ComputeTxFee(tx) + relayer.Balance.Sub(relayer.Balance, txFee) + + return tx +} + func createAndSendSimpleTransaction( nodes []*integrationTests.TestProcessorNode, player *integrationTests.TestWalletAccount, diff --git a/integrationTests/multiShard/relayedTx/relayedTx_test.go b/integrationTests/multiShard/relayedTx/relayedTx_test.go index 41ece5b81eb..5d54e2133cc 100644 --- a/integrationTests/multiShard/relayedTx/relayedTx_test.go +++ b/integrationTests/multiShard/relayedTx/relayedTx_test.go @@ -33,20 +33,24 @@ type createAndSendRelayedAndUserTxFuncType = func( func TestRelayedTransactionInMultiShardEnvironmentWithNormalTx(t *testing.T) { t.Run("relayed v1", testRelayedTransactionInMultiShardEnvironmentWithNormalTx(CreateAndSendRelayedAndUserTx, false)) + t.Run("relayed v3", testRelayedTransactionInMultiShardEnvironmentWithNormalTx(CreateAndSendRelayedAndUserTxV3, true)) } func TestRelayedTransactionInMultiShardEnvironmentWithSmartContractTX(t *testing.T) { t.Run("relayed v1", testRelayedTransactionInMultiShardEnvironmentWithSmartContractTX(CreateAndSendRelayedAndUserTx, false)) t.Run("relayed v2", testRelayedTransactionInMultiShardEnvironmentWithSmartContractTX(CreateAndSendRelayedAndUserTxV2, false)) + t.Run("relayed v3", testRelayedTransactionInMultiShardEnvironmentWithSmartContractTX(CreateAndSendRelayedAndUserTxV3, true)) } func TestRelayedTransactionInMultiShardEnvironmentWithESDTTX(t *testing.T) { t.Run("relayed v1", testRelayedTransactionInMultiShardEnvironmentWithESDTTX(CreateAndSendRelayedAndUserTx, false)) t.Run("relayed v2", testRelayedTransactionInMultiShardEnvironmentWithESDTTX(CreateAndSendRelayedAndUserTxV2, false)) + t.Run("relayed v3", testRelayedTransactionInMultiShardEnvironmentWithESDTTX(CreateAndSendRelayedAndUserTxV3, true)) } func TestRelayedTransactionInMultiShardEnvironmentWithAttestationContract(t *testing.T) { t.Run("relayed v1", testRelayedTransactionInMultiShardEnvironmentWithAttestationContract(CreateAndSendRelayedAndUserTx, false)) + t.Run("relayed v3", testRelayedTransactionInMultiShardEnvironmentWithAttestationContract(CreateAndSendRelayedAndUserTxV3, true)) } func testRelayedTransactionInMultiShardEnvironmentWithNormalTx( @@ -138,6 +142,8 @@ func testRelayedTransactionInMultiShardEnvironmentWithSmartContractTX( receiverAddress1 := []byte("12345678901234567890123456789012") receiverAddress2 := []byte("12345678901234567890123456789011") + integrationTests.MintAllPlayers(nodes, players, big.NewInt(1)) + ownerNode := nodes[0] initialSupply := "00" + hex.EncodeToString(big.NewInt(100000000000).Bytes()) scCode := wasm.GetSCCode("../../vm/wasm/testdata/erc20-c-03/wrc20_wasm.wasm") diff --git a/integrationTests/testProcessorNode.go b/integrationTests/testProcessorNode.go index dc828291384..80e8b5f0767 100644 --- a/integrationTests/testProcessorNode.go +++ b/integrationTests/testProcessorNode.go @@ -2592,22 +2592,29 @@ func (tpn *TestProcessorNode) SendTransaction(tx *dataTransaction.Transaction) ( if len(tx.GuardianAddr) == TestAddressPubkeyConverter.Len() { guardianAddress = TestAddressPubkeyConverter.SilentEncode(tx.GuardianAddr, log) } + + relayerAddress := "" + if len(tx.RelayerAddr) == TestAddressPubkeyConverter.Len() { + relayerAddress = TestAddressPubkeyConverter.SilentEncode(tx.RelayerAddr, log) + } createTxArgs := &external.ArgsCreateTransaction{ - Nonce: tx.Nonce, - Value: tx.Value.String(), - Receiver: encodedRcvAddr, - ReceiverUsername: nil, - Sender: encodedSndAddr, - SenderUsername: nil, - GasPrice: tx.GasPrice, - GasLimit: tx.GasLimit, - DataField: tx.Data, - SignatureHex: hex.EncodeToString(tx.Signature), - ChainID: string(tx.ChainID), - Version: tx.Version, - Options: tx.Options, - Guardian: guardianAddress, - GuardianSigHex: hex.EncodeToString(tx.GuardianSignature), + Nonce: tx.Nonce, + Value: tx.Value.String(), + Receiver: encodedRcvAddr, + ReceiverUsername: nil, + Sender: encodedSndAddr, + SenderUsername: nil, + GasPrice: tx.GasPrice, + GasLimit: tx.GasLimit, + DataField: tx.Data, + SignatureHex: hex.EncodeToString(tx.Signature), + ChainID: string(tx.ChainID), + Version: tx.Version, + Options: tx.Options, + Guardian: guardianAddress, + GuardianSigHex: hex.EncodeToString(tx.GuardianSignature), + Relayer: relayerAddress, + RelayerSignatureHex: hex.EncodeToString(tx.RelayerSignature), } tx, txHash, err := tpn.Node.CreateTransaction(createTxArgs) if err != nil { diff --git a/node/external/dtos.go b/node/external/dtos.go index f884d8d32c9..b1789054f5e 100644 --- a/node/external/dtos.go +++ b/node/external/dtos.go @@ -2,19 +2,21 @@ package external // ArgsCreateTransaction defines arguments for creating a transaction type ArgsCreateTransaction struct { - Nonce uint64 - Value string - Receiver string - ReceiverUsername []byte - Sender string - SenderUsername []byte - GasPrice uint64 - GasLimit uint64 - DataField []byte - SignatureHex string - ChainID string - Version uint32 - Options uint32 - Guardian string - GuardianSigHex string + Nonce uint64 + Value string + Receiver string + ReceiverUsername []byte + Sender string + SenderUsername []byte + GasPrice uint64 + GasLimit uint64 + DataField []byte + SignatureHex string + ChainID string + Version uint32 + Options uint32 + Guardian string + GuardianSigHex string + Relayer string + RelayerSignatureHex string } diff --git a/node/external/transactionAPI/apiTransactionProcessor.go b/node/external/transactionAPI/apiTransactionProcessor.go index c67ad1cb445..c6c568d46bf 100644 --- a/node/external/transactionAPI/apiTransactionProcessor.go +++ b/node/external/transactionAPI/apiTransactionProcessor.go @@ -199,9 +199,9 @@ func (atp *apiTransactionProcessor) populateComputedFieldInitiallyPaidFee(tx *tr tx.InitiallyPaidFee = fee.String() isFeeFixActive := atp.enableEpochsHandler.IsFlagEnabledInEpoch(common.FixRelayedBaseCostFlag, tx.Epoch) - isRelayedAfterFix := tx.IsRelayed && isFeeFixActive + _, fee, isRelayedV1V2 := atp.gasUsedAndFeeProcessor.getFeeOfRelayedV1V2(tx) + isRelayedAfterFix := tx.IsRelayed && isFeeFixActive && isRelayedV1V2 if isRelayedAfterFix { - _, fee, _ = atp.gasUsedAndFeeProcessor.getFeeOfRelayed(tx) tx.InitiallyPaidFee = fee.String() } } @@ -399,12 +399,19 @@ func (atp *apiTransactionProcessor) getFieldGettersForTx(wrappedTx *txcache.Wrap } guardedTx, isGuardedTx := wrappedTx.Tx.(data.GuardedTransactionHandler) - if isGuardedTx { + if isGuardedTx && len(guardedTx.GetGuardianAddr()) > 0 { fieldGetters[signatureField] = hex.EncodeToString(guardedTx.GetSignature()) fieldGetters[guardianField] = atp.addressPubKeyConverter.SilentEncode(guardedTx.GetGuardianAddr(), log) fieldGetters[guardianSignatureField] = hex.EncodeToString(guardedTx.GetGuardianSignature()) } + relayedTx, ok := wrappedTx.Tx.(data.RelayedTransactionHandler) + if ok && len(relayedTx.GetRelayerAddr()) > 0 { + fieldGetters[signatureField] = hex.EncodeToString(relayedTx.GetSignature()) + fieldGetters[relayerField] = atp.addressPubKeyConverter.SilentEncode(relayedTx.GetRelayerAddr(), log) + fieldGetters[relayerSignatureField] = hex.EncodeToString(relayedTx.GetRelayerSignature()) + } + return fieldGetters } diff --git a/node/external/transactionAPI/fieldsHandler.go b/node/external/transactionAPI/fieldsHandler.go index 4f837968cb7..8debf6f6ceb 100644 --- a/node/external/transactionAPI/fieldsHandler.go +++ b/node/external/transactionAPI/fieldsHandler.go @@ -19,6 +19,8 @@ const ( guardianSignatureField = "guardiansignature" senderShardID = "sendershard" receiverShardID = "receivershard" + relayerField = "relayer" + relayerSignatureField = "relayersignature" wildCard = "*" separator = "," diff --git a/node/external/transactionAPI/gasUsedAndFeeProcessor.go b/node/external/transactionAPI/gasUsedAndFeeProcessor.go index c4cd9578394..c5aa49e05c1 100644 --- a/node/external/transactionAPI/gasUsedAndFeeProcessor.go +++ b/node/external/transactionAPI/gasUsedAndFeeProcessor.go @@ -50,17 +50,26 @@ func (gfp *gasUsedAndFeeProcessor) computeAndAttachGasUsedAndFee(tx *transaction tx.Fee = tx.InitiallyPaidFee } - userTx, initialTotalFee, isRelayed := gfp.getFeeOfRelayed(tx) - isRelayedAfterFix := isRelayed && isFeeFixActive + userTx, initialTotalFee, isRelayedV1V2 := gfp.getFeeOfRelayedV1V2(tx) + isRelayedAfterFix := isRelayedV1V2 && isFeeFixActive if isRelayedAfterFix { tx.InitiallyPaidFee = initialTotalFee.String() tx.Fee = initialTotalFee.String() tx.GasUsed = big.NewInt(0).Div(initialTotalFee, big.NewInt(0).SetUint64(tx.GasPrice)).Uint64() } + hasValidRelayer := len(tx.RelayerAddress) == len(tx.Sender) && len(tx.RelayerAddress) > 0 + hasValidRelayerSignature := len(tx.RelayerSignature) == len(tx.Signature) && len(tx.RelayerSignature) > 0 + isRelayedV3 := hasValidRelayer && hasValidRelayerSignature hasRefundForSender := false for _, scr := range tx.SmartContractResults { - if !scr.IsRefund || scr.RcvAddr != tx.Sender { + if !scr.IsRefund { + continue + } + if !isRelayedV3 && scr.RcvAddr != tx.Sender { + continue + } + if isRelayedV3 && scr.RcvAddr != tx.RelayerAddress { continue } @@ -72,11 +81,18 @@ func (gfp *gasUsedAndFeeProcessor) computeAndAttachGasUsedAndFee(tx *transaction gfp.prepareTxWithResultsBasedOnLogs(tx, userTx, hasRefundForSender) } -func (gfp *gasUsedAndFeeProcessor) getFeeOfRelayed(tx *transaction.ApiTransactionResult) (*transaction.ApiTransactionResult, *big.Int, bool) { +func (gfp *gasUsedAndFeeProcessor) getFeeOfRelayedV1V2(tx *transaction.ApiTransactionResult) (*transaction.ApiTransactionResult, *big.Int, bool) { if !tx.IsRelayed { return nil, nil, false } + hasValidRelayer := len(tx.RelayerAddress) == len(tx.Sender) && len(tx.RelayerAddress) > 0 + hasValidRelayerSignature := len(tx.RelayerSignature) == len(tx.Signature) && len(tx.RelayerSignature) > 0 + isRelayedV3 := hasValidRelayer && hasValidRelayerSignature + if isRelayedV3 { + return nil, nil, false + } + if len(tx.Data) == 0 { return nil, nil, false } @@ -173,7 +189,10 @@ func (gfp *gasUsedAndFeeProcessor) setGasUsedAndFeeBaseOnRefundValue( userTx *transaction.ApiTransactionResult, refund *big.Int, ) { - if !check.IfNilReflect(userTx) && gfp.enableEpochsHandler.IsFlagEnabledInEpoch(common.FixRelayedBaseCostFlag, tx.Epoch) { + isRelayedV3 := len(tx.RelayerAddress) == len(tx.Sender) && + len(tx.RelayerSignature) == len(tx.Signature) + isValidUserTxAfterBaseCostActivation := !check.IfNilReflect(userTx) && gfp.enableEpochsHandler.IsFlagEnabledInEpoch(common.FixRelayedBaseCostFlag, tx.Epoch) + if isValidUserTxAfterBaseCostActivation && !isRelayedV3 { gasUsed, fee := gfp.feeComputer.ComputeGasUsedAndFeeBasedOnRefundValue(userTx, refund) gasUsedRelayedTx := gfp.feeComputer.ComputeGasLimit(tx) feeRelayedTx := gfp.feeComputer.ComputeTxFeeBasedOnGasUsed(tx, gasUsedRelayedTx) diff --git a/node/external/transactionAPI/unmarshaller.go b/node/external/transactionAPI/unmarshaller.go index c9526217f4f..24d8961885b 100644 --- a/node/external/transactionAPI/unmarshaller.go +++ b/node/external/transactionAPI/unmarshaller.go @@ -101,7 +101,11 @@ func (tu *txUnmarshaller) unmarshalTransaction(txBytes []byte, txType transactio } apiTx.ReceiversShardIDs = res.ReceiversShardID - apiTx.IsRelayed = res.IsRelayed + + hasValidRelayer := len(apiTx.RelayerAddress) == len(apiTx.Sender) && len(apiTx.RelayerAddress) > 0 + hasValidRelayerSignature := len(apiTx.RelayerSignature) == len(apiTx.Signature) && len(apiTx.RelayerSignature) > 0 + isRelayedV3 := hasValidRelayer && hasValidRelayerSignature + apiTx.IsRelayed = res.IsRelayed || isRelayedV3 return apiTx, nil } @@ -132,6 +136,12 @@ func (tu *txUnmarshaller) prepareNormalTx(tx *transaction.Transaction) *transact apiTx.GuardianAddr = tu.addressPubKeyConverter.SilentEncode(tx.GuardianAddr, log) apiTx.GuardianSignature = hex.EncodeToString(tx.GuardianSignature) } + if len(tx.RelayerAddr) > 0 { + apiTx.RelayerAddress = tu.addressPubKeyConverter.SilentEncode(tx.RelayerAddr, log) + } + if len(tx.RelayerSignature) > 0 { + apiTx.RelayerSignature = hex.EncodeToString(tx.RelayerSignature) + } return apiTx } @@ -162,6 +172,12 @@ func (tu *txUnmarshaller) prepareInvalidTx(tx *transaction.Transaction) *transac apiTx.GuardianAddr = tu.addressPubKeyConverter.SilentEncode(tx.GuardianAddr, log) apiTx.GuardianSignature = hex.EncodeToString(tx.GuardianSignature) } + if len(tx.RelayerAddr) > 0 { + apiTx.RelayerAddress = tu.addressPubKeyConverter.SilentEncode(tx.RelayerAddr, log) + } + if len(tx.RelayerSignature) > 0 { + apiTx.RelayerSignature = hex.EncodeToString(tx.RelayerSignature) + } return apiTx } diff --git a/node/metrics/metrics.go b/node/metrics/metrics.go index 7d828d26394..5c74f42eacd 100644 --- a/node/metrics/metrics.go +++ b/node/metrics/metrics.go @@ -202,6 +202,7 @@ func InitConfigMetrics( appStatusHandler.SetUInt64Value(common.MetricCryptoOpcodesV2EnableEpoch, uint64(enableEpochs.CryptoOpcodesV2EnableEpoch)) appStatusHandler.SetUInt64Value(common.MetricMultiESDTNFTTransferAndExecuteByUserEnableEpoch, uint64(enableEpochs.MultiESDTNFTTransferAndExecuteByUserEnableEpoch)) appStatusHandler.SetUInt64Value(common.MetricFixRelayedMoveBalanceToNonPayableSCEnableEpoch, uint64(enableEpochs.FixRelayedMoveBalanceToNonPayableSCEnableEpoch)) + appStatusHandler.SetUInt64Value(common.MetricRelayedTransactionsV3EnableEpoch, uint64(enableEpochs.RelayedTransactionsV3EnableEpoch)) for i, nodesChangeConfig := range enableEpochs.MaxNodesChangeEnableEpoch { epochEnable := fmt.Sprintf("%s%d%s", common.MetricMaxNodesChangeEnableEpoch, i, common.EpochEnableSuffix) diff --git a/node/metrics/metrics_test.go b/node/metrics/metrics_test.go index 1aa3bd7be2e..3ffe2cf7a5a 100644 --- a/node/metrics/metrics_test.go +++ b/node/metrics/metrics_test.go @@ -211,6 +211,7 @@ func TestInitConfigMetrics(t *testing.T) { FixRelayedBaseCostEnableEpoch: 104, MultiESDTNFTTransferAndExecuteByUserEnableEpoch: 105, FixRelayedMoveBalanceToNonPayableSCEnableEpoch: 106, + RelayedTransactionsV3EnableEpoch: 107, MaxNodesChangeEnableEpoch: []config.MaxNodesChangeConfig{ { EpochEnable: 0, @@ -332,6 +333,7 @@ func TestInitConfigMetrics(t *testing.T) { "erd_fix_relayed_base_cost_enable_epoch": uint32(104), "erd_multi_esdt_transfer_execute_by_user_enable_epoch": uint32(105), "erd_fix_relayed_move_balance_to_non_payable_sc_enable_epoch": uint32(106), + "erd_relayed_transactions_v3_enable_epoch": uint32(107), "erd_max_nodes_change_enable_epoch": nil, "erd_total_supply": "12345", "erd_hysteresis": "0.100000", diff --git a/node/node.go b/node/node.go index a652e80be60..ca07f4a0acf 100644 --- a/node/node.go +++ b/node/node.go @@ -846,6 +846,9 @@ func (n *Node) CreateTransaction(txArgs *external.ArgsCreateTransaction) (*trans if len(txArgs.GuardianSigHex) > n.addressSignatureHexSize { return nil, nil, fmt.Errorf("%w for guardian signature", ErrInvalidSignatureLength) } + if len(txArgs.RelayerSignatureHex) > n.addressSignatureHexSize { + return nil, nil, fmt.Errorf("%w for relayer signature", ErrInvalidSignatureLength) + } if uint32(len(txArgs.Receiver)) > n.coreComponents.EncodedAddressLen() { return nil, nil, fmt.Errorf("%w for receiver", ErrInvalidAddressLength) @@ -856,6 +859,9 @@ func (n *Node) CreateTransaction(txArgs *external.ArgsCreateTransaction) (*trans if uint32(len(txArgs.Guardian)) > n.coreComponents.EncodedAddressLen() { return nil, nil, fmt.Errorf("%w for guardian", ErrInvalidAddressLength) } + if uint32(len(txArgs.Relayer)) > n.coreComponents.EncodedAddressLen() { + return nil, nil, fmt.Errorf("%w for relayer", ErrInvalidAddressLength) + } if len(txArgs.SenderUsername) > core.MaxUserNameLength { return nil, nil, ErrInvalidSenderUsernameLength } @@ -912,6 +918,20 @@ func (n *Node) CreateTransaction(txArgs *external.ArgsCreateTransaction) (*trans return nil, nil, err } } + if len(txArgs.Relayer) > 0 { + relayerAddress, errDecode := addrPubKeyConverter.Decode(txArgs.Relayer) + if errDecode != nil { + return nil, nil, fmt.Errorf("%w while decoding relayer address", errDecode) + } + tx.RelayerAddr = relayerAddress + } + if len(txArgs.RelayerSignatureHex) > 0 { + relayerSigBytes, errDecodeString := hex.DecodeString(txArgs.RelayerSignatureHex) + if errDecodeString != nil { + return nil, nil, fmt.Errorf("%w while decoding relayer signature", errDecodeString) + } + tx.RelayerSignature = relayerSigBytes + } var txHash []byte txHash, err = core.CalculateHash(n.coreComponents.InternalMarshalizer(), n.coreComponents.Hasher(), tx) diff --git a/outport/process/transactionsfee/transactionsFeeProcessor.go b/outport/process/transactionsfee/transactionsFeeProcessor.go index 30bcf0fca76..8dfaa1bf338 100644 --- a/outport/process/transactionsfee/transactionsFeeProcessor.go +++ b/outport/process/transactionsfee/transactionsFeeProcessor.go @@ -271,7 +271,15 @@ func (tep *transactionsFeeProcessor) setGasUsedAndFeeBasedOnRefundValue( refund *big.Int, epoch uint32, ) { - if !check.IfNil(userTx) && tep.enableEpochsHandler.IsFlagEnabledInEpoch(common.FixRelayedBaseCostFlag, epoch) { + tx := txWithResults.GetTxHandler() + relayedTx, isRelayedV3 := tx.(data.RelayedTransactionHandler) + if isRelayedV3 { + hasValidRelayer := len(relayedTx.GetRelayerAddr()) == len(tx.GetSndAddr()) && len(relayedTx.GetRelayerAddr()) > 0 + hasValidRelayerSignature := len(relayedTx.GetRelayerSignature()) == len(relayedTx.GetSignature()) && len(relayedTx.GetRelayerSignature()) > 0 + isRelayedV3 = hasValidRelayer && hasValidRelayerSignature + } + isValidUserTxAfterBaseCostActivation := !check.IfNilReflect(userTx) && tep.enableEpochsHandler.IsFlagEnabledInEpoch(common.FixRelayedBaseCostFlag, epoch) + if isValidUserTxAfterBaseCostActivation && !isRelayedV3 { gasUsed, fee := tep.txFeeCalculator.ComputeGasUsedAndFeeBasedOnRefundValue(userTx, refund) tx := txWithResults.GetTxHandler() diff --git a/process/constants.go b/process/constants.go index f75e7b882ee..5d7960c70c8 100644 --- a/process/constants.go +++ b/process/constants.go @@ -36,6 +36,8 @@ const ( RelayedTx // RelayedTxV2 defines the ID of a slim relayed transaction version RelayedTxV2 + // RelayedTxV3 defines the ID of a relayed transaction v3 + RelayedTxV3 // RewardTx defines ID of a reward transaction RewardTx // InvalidTransaction defines unknown transaction type @@ -56,6 +58,8 @@ func (transactionType TransactionType) String() string { return "RelayedTx" case RelayedTxV2: return "RelayedTxV2" + case RelayedTxV3: + return "RelayedTxV3" case RewardTx: return "RewardTx" case InvalidTransaction: diff --git a/process/coordinator/transactionType.go b/process/coordinator/transactionType.go index 32081a1ac0e..dd648a4dd54 100644 --- a/process/coordinator/transactionType.go +++ b/process/coordinator/transactionType.go @@ -83,6 +83,16 @@ func (tth *txTypeHandler) ComputeTransactionType(tx data.TransactionHandler) (pr return process.InvalidTransaction, process.InvalidTransaction } + relayedTxV3, ok := tx.(data.RelayedTransactionHandler) + if ok { + hasValidRelayer := len(relayedTxV3.GetRelayerAddr()) == len(tx.GetSndAddr()) && len(relayedTxV3.GetRelayerAddr()) > 0 + hasValidRelayerSignature := len(relayedTxV3.GetRelayerSignature()) == len(relayedTxV3.GetSignature()) && len(relayedTxV3.GetRelayerSignature()) > 0 + isRelayedV3 := hasValidRelayer && hasValidRelayerSignature + if isRelayedV3 { + return process.RelayedTxV3, process.RelayedTxV3 + } + } + isEmptyAddress := tth.isDestAddressEmpty(tx) if isEmptyAddress { if len(tx.GetData()) > 0 { diff --git a/process/coordinator/transactionType_test.go b/process/coordinator/transactionType_test.go index 918b6069212..705b45c78e8 100644 --- a/process/coordinator/transactionType_test.go +++ b/process/coordinator/transactionType_test.go @@ -466,6 +466,34 @@ func TestTxTypeHandler_ComputeTransactionTypeRelayedV2Func(t *testing.T) { assert.Equal(t, process.RelayedTxV2, txTypeCross) } +func TestTxTypeHandler_ComputeTransactionTypeRelayedV3(t *testing.T) { + t.Parallel() + + tx := &transaction.Transaction{} + tx.Nonce = 0 + tx.SndAddr = []byte("000") + tx.RcvAddr = []byte("001") + tx.Value = big.NewInt(45) + tx.RelayerAddr = []byte("002") + tx.Signature = []byte("ssig") + tx.RelayerSignature = []byte("rsig") + + arg := createMockArguments() + arg.PubkeyConverter = &testscommon.PubkeyConverterStub{ + LenCalled: func() int { + return len(tx.RcvAddr) + }, + } + tth, err := NewTxTypeHandler(arg) + + assert.NotNil(t, tth) + assert.Nil(t, err) + + txTypeIn, txTypeCross := tth.ComputeTransactionType(tx) + assert.Equal(t, process.RelayedTxV3, txTypeIn) + assert.Equal(t, process.RelayedTxV3, txTypeCross) +} + func TestTxTypeHandler_ComputeTransactionTypeForSCRCallBack(t *testing.T) { t.Parallel() diff --git a/process/dataValidators/txValidator.go b/process/dataValidators/txValidator.go index 9c72be1d89a..b95c1bca2b7 100644 --- a/process/dataValidators/txValidator.go +++ b/process/dataValidators/txValidator.go @@ -5,6 +5,7 @@ import ( "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-core-go/core/check" + "github.com/multiversx/mx-chain-core-go/data" "github.com/multiversx/mx-chain-go/process" "github.com/multiversx/mx-chain-go/sharding" "github.com/multiversx/mx-chain-go/state" @@ -91,24 +92,46 @@ func (txv *txValidator) checkAccount( return err } - account, err := txv.getSenderUserAccount(interceptedTx, accountHandler) + feePayerAccount, err := txv.getFeePayerAccount(interceptedTx, accountHandler) if err != nil { return err } - return txv.checkBalance(interceptedTx, account) + return txv.checkBalance(interceptedTx, feePayerAccount) } -func (txv *txValidator) getSenderUserAccount( +func (txv *txValidator) getFeePayerAccount( interceptedTx process.InterceptedTransactionHandler, accountHandler vmcommon.AccountHandler, ) (state.UserAccountHandler, error) { - senderAddress := interceptedTx.SenderAddress() - account, ok := accountHandler.(state.UserAccountHandler) + payerAddress := interceptedTx.SenderAddress() + payerAccount := accountHandler + + tx := interceptedTx.Transaction() + relayedTx, ok := tx.(data.RelayedTransactionHandler) + if ok { + hasValidRelayer := len(relayedTx.GetRelayerAddr()) == len(tx.GetSndAddr()) && len(relayedTx.GetRelayerAddr()) > 0 + hasValidRelayerSignature := len(relayedTx.GetRelayerSignature()) == len(relayedTx.GetSignature()) && len(relayedTx.GetRelayerSignature()) > 0 + isRelayedV3 := hasValidRelayer && hasValidRelayerSignature + if isRelayedV3 { + payerAddress = relayedTx.GetRelayerAddr() + relayerAccount, err := txv.accounts.GetExistingAccount(payerAddress) + if err != nil { + return nil, fmt.Errorf("%w for address %s and shard %d, err: %s", + process.ErrAccountNotFound, + txv.pubKeyConverter.SilentEncode(payerAddress, log), + txv.shardCoordinator.SelfId(), + err.Error(), + ) + } + payerAccount = relayerAccount + } + } + account, ok := payerAccount.(state.UserAccountHandler) if !ok { return nil, fmt.Errorf("%w, account is not of type *state.Account, address: %s", process.ErrWrongTypeAssertion, - txv.pubKeyConverter.SilentEncode(senderAddress, log), + txv.pubKeyConverter.SilentEncode(payerAddress, log), ) } return account, nil @@ -118,10 +141,9 @@ func (txv *txValidator) checkBalance(interceptedTx process.InterceptedTransactio accountBalance := account.GetBalance() txFee := interceptedTx.Fee() if accountBalance.Cmp(txFee) < 0 { - senderAddress := interceptedTx.SenderAddress() return fmt.Errorf("%w, for address: %s, wanted %v, have %v", process.ErrInsufficientFunds, - txv.pubKeyConverter.SilentEncode(senderAddress, log), + txv.pubKeyConverter.SilentEncode(account.AddressBytes(), log), txFee, accountBalance, ) diff --git a/process/dataValidators/txValidator_test.go b/process/dataValidators/txValidator_test.go index 551b18928d1..a45acf2b434 100644 --- a/process/dataValidators/txValidator_test.go +++ b/process/dataValidators/txValidator_test.go @@ -1,6 +1,7 @@ package dataValidators_test import ( + "bytes" "errors" "math/big" "strconv" @@ -269,31 +270,100 @@ func TestTxValidator_CheckTxValidityTxNonceIsTooHigh(t *testing.T) { func TestTxValidator_CheckTxValidityAccountBalanceIsLessThanTxTotalValueShouldReturnFalse(t *testing.T) { t.Parallel() - accountNonce := uint64(0) - txNonce := uint64(1) - fee := big.NewInt(1000) - accountBalance := big.NewInt(10) + t.Run("normal tx should return false for sender", func(t *testing.T) { + t.Parallel() - adb := getAccAdapter(accountNonce, accountBalance) - shardCoordinator := createMockCoordinator("_", 0) - maxNonceDeltaAllowed := 100 - txValidator, err := dataValidators.NewTxValidator( - adb, - shardCoordinator, - &testscommon.WhiteListHandlerStub{}, - testscommon.NewPubkeyConverterMock(32), - &testscommon.TxVersionCheckerStub{}, - maxNonceDeltaAllowed, - ) - assert.Nil(t, err) + accountNonce := uint64(0) + txNonce := uint64(1) + fee := big.NewInt(1000) + accountBalance := big.NewInt(10) - addressMock := []byte("address") - currentShard := uint32(0) - txValidatorHandler := getInterceptedTxHandler(currentShard, currentShard, txNonce, addressMock, fee) + providedSenderAddress := []byte("address") + adb := &stateMock.AccountsStub{} + adb.GetExistingAccountCalled = func(address []byte) (handler vmcommon.AccountHandler, e error) { + require.True(t, bytes.Equal(providedSenderAddress, address)) - result := txValidator.CheckTxValidity(txValidatorHandler) - assert.NotNil(t, result) - assert.True(t, errors.Is(result, process.ErrInsufficientFunds)) + acc, _ := accounts.NewUserAccount(address, &trie.DataTrieTrackerStub{}, &trie.TrieLeafParserStub{}) + acc.Nonce = accountNonce + acc.Balance = accountBalance + + return acc, nil + } + + shardCoordinator := createMockCoordinator("_", 0) + maxNonceDeltaAllowed := 100 + txValidator, err := dataValidators.NewTxValidator( + adb, + shardCoordinator, + &testscommon.WhiteListHandlerStub{}, + testscommon.NewPubkeyConverterMock(32), + &testscommon.TxVersionCheckerStub{}, + maxNonceDeltaAllowed, + ) + assert.Nil(t, err) + + currentShard := uint32(0) + txValidatorHandler := getInterceptedTxHandler(currentShard, currentShard, txNonce, providedSenderAddress, fee) + + result := txValidator.CheckTxValidity(txValidatorHandler) + assert.NotNil(t, result) + assert.True(t, errors.Is(result, process.ErrInsufficientFunds)) + }) + t.Run("relayed tx v3 should return false for relayer", func(t *testing.T) { + t.Parallel() + + accountNonce := uint64(0) + txNonce := uint64(1) + fee := big.NewInt(1000) + accountBalance := big.NewInt(10) + + providedRelayerAddress := []byte("relayer") + providedSenderAddress := []byte("address") + adb := &stateMock.AccountsStub{} + cnt := 0 + adb.GetExistingAccountCalled = func(address []byte) (handler vmcommon.AccountHandler, e error) { + cnt++ + if cnt == 1 { + require.True(t, bytes.Equal(providedSenderAddress, address)) + } else { + require.True(t, bytes.Equal(providedRelayerAddress, address)) + } + + acc, _ := accounts.NewUserAccount(address, &trie.DataTrieTrackerStub{}, &trie.TrieLeafParserStub{}) + acc.Nonce = accountNonce + acc.Balance = accountBalance + + return acc, nil + } + + shardCoordinator := createMockCoordinator("_", 0) + maxNonceDeltaAllowed := 100 + txValidator, err := dataValidators.NewTxValidator( + adb, + shardCoordinator, + &testscommon.WhiteListHandlerStub{}, + testscommon.NewPubkeyConverterMock(32), + &testscommon.TxVersionCheckerStub{}, + maxNonceDeltaAllowed, + ) + assert.Nil(t, err) + + currentShard := uint32(0) + txValidatorHandler := getInterceptedTxHandler(currentShard, currentShard, txNonce, providedSenderAddress, fee) + txValidatorHandlerStub, ok := txValidatorHandler.(*mock.InterceptedTxHandlerStub) + require.True(t, ok) + txValidatorHandlerStub.TransactionCalled = func() data.TransactionHandler { + return &transaction.Transaction{ + SndAddr: providedSenderAddress, + Signature: []byte("address sig"), + RelayerAddr: providedRelayerAddress, + RelayerSignature: []byte("relayer sig"), + } + } + result := txValidator.CheckTxValidity(txValidatorHandler) + assert.NotNil(t, result) + assert.True(t, errors.Is(result, process.ErrInsufficientFunds)) + }) } func TestTxValidator_CheckTxValidityAccountNotExitsShouldReturnFalse(t *testing.T) { diff --git a/process/economics/economicsData.go b/process/economics/economicsData.go index 5b7ce045237..4da39f94862 100644 --- a/process/economics/economicsData.go +++ b/process/economics/economicsData.go @@ -488,15 +488,24 @@ func (ed *economicsData) ComputeGasLimit(tx data.TransactionWithFeeHandler) uint return ed.ComputeGasLimitInEpoch(tx, currentEpoch) } -// ComputeGasLimitInEpoch returns the gas limit need by the provided transaction in order to be executed in a specific epoch +// ComputeGasLimitInEpoch returns the gas limit needed by the provided transaction in order to be executed in a specific epoch func (ed *economicsData) ComputeGasLimitInEpoch(tx data.TransactionWithFeeHandler, epoch uint32) uint64 { gasLimit := ed.getMinGasLimit(epoch) dataLen := uint64(len(tx.GetData())) gasLimit += dataLen * ed.gasPerDataByte txInstance, ok := tx.(*transaction.Transaction) - if ok && ed.txVersionHandler.IsGuardedTransaction(txInstance) { - gasLimit += ed.getExtraGasLimitGuardedTx(epoch) + if ok { + if ed.txVersionHandler.IsGuardedTransaction(txInstance) { + gasLimit += ed.getExtraGasLimitGuardedTx(epoch) + } + + hasValidRelayer := len(txInstance.GetRelayerAddr()) == len(txInstance.GetSndAddr()) && len(txInstance.GetRelayerAddr()) > 0 + hasValidRelayerSignature := len(txInstance.GetRelayerSignature()) == len(txInstance.GetSignature()) && len(txInstance.GetRelayerSignature()) > 0 + isRelayedV3 := hasValidRelayer && hasValidRelayerSignature + if isRelayedV3 { + gasLimit += ed.MinGasLimitInEpoch(epoch) + } } return gasLimit diff --git a/process/errors.go b/process/errors.go index 83e8095dcb3..d75093d8cd9 100644 --- a/process/errors.go +++ b/process/errors.go @@ -1229,3 +1229,9 @@ var ErrNilSentSignatureTracker = errors.New("nil sent signature tracker") // ErrTransferAndExecuteByUserAddressesAreNil signals that transfer and execute by user addresses are nil var ErrTransferAndExecuteByUserAddressesAreNil = errors.New("transfer and execute by user addresses are nil") + +// ErrRelayedTxV3Disabled signals that relayed tx v3 are disabled +var ErrRelayedTxV3Disabled = errors.New("relayed tx v3 are disabled") + +// ErrGuardedRelayerNotAllowed signals that the provided relayer is guarded +var ErrGuardedRelayerNotAllowed = errors.New("guarded relayer not allowed") diff --git a/process/transaction/baseProcess.go b/process/transaction/baseProcess.go index b1e95a71339..72ae41a3bd6 100644 --- a/process/transaction/baseProcess.go +++ b/process/transaction/baseProcess.go @@ -152,10 +152,15 @@ func (txProc *baseTxProcessor) checkTxValues( txFee = txProc.economicsFee.ComputeTxFee(tx) } - if acntSnd.GetBalance().Cmp(txFee) < 0 { + feePayer, err := txProc.getFeePayer(tx, acntSnd) + if err != nil { + return err + } + + if feePayer.GetBalance().Cmp(txFee) < 0 { return fmt.Errorf("%w, has: %s, wanted: %s", process.ErrInsufficientFee, - acntSnd.GetBalance().String(), + feePayer.GetBalance().String(), txFee.String(), ) } @@ -167,13 +172,29 @@ func (txProc *baseTxProcessor) checkTxValues( } cost := big.NewInt(0).Add(txFee, tx.Value) - if acntSnd.GetBalance().Cmp(cost) < 0 { + if feePayer.GetBalance().Cmp(cost) < 0 { return process.ErrInsufficientFunds } return nil } +func (txProc *baseTxProcessor) getFeePayer( + tx *transaction.Transaction, + acntSnd state.UserAccountHandler, +) (state.UserAccountHandler, error) { + if !isRelayedTxV3(tx) { + return acntSnd, nil + } + + acntRelayer, _, err := txProc.getAccounts(tx.RelayerAddr, tx.RelayerAddr) + if err != nil { + return nil, err + } + + return acntRelayer, nil +} + func (txProc *baseTxProcessor) computeInnerTxFee(tx *transaction.Transaction) *big.Int { if txProc.enableEpochsHandler.IsFlagEnabled(common.FixRelayedBaseCostFlag) { return txProc.computeInnerTxFeeAfterBaseCostFix(tx) diff --git a/process/transaction/export_test.go b/process/transaction/export_test.go index cd657c3991d..f6c154dc6d1 100644 --- a/process/transaction/export_test.go +++ b/process/transaction/export_test.go @@ -55,6 +55,7 @@ func (txProc *txProcessor) ProcessUserTx( userTx *transaction.Transaction, relayedTxValue *big.Int, relayedNonce uint64, + relayerAddr []byte, originalTxHash []byte, ) (vmcommon.ReturnCode, error) { return txProc.processUserTx( @@ -62,6 +63,7 @@ func (txProc *txProcessor) ProcessUserTx( userTx, relayedTxValue, relayedNonce, + relayerAddr, originalTxHash) } diff --git a/process/transaction/interceptedTransaction.go b/process/transaction/interceptedTransaction.go index 75b9a65ae7c..890939b621d 100644 --- a/process/transaction/interceptedTransaction.go +++ b/process/transaction/interceptedTransaction.go @@ -189,6 +189,11 @@ func (inTx *InterceptedTransaction) CheckValidity() error { return err } + err = inTx.verifyIfRelayedTxV3(inTx.tx) + if err != nil { + return err + } + err = inTx.verifyIfRelayedTx(inTx.tx) if err != nil { return err @@ -205,8 +210,12 @@ func (inTx *InterceptedTransaction) CheckValidity() error { return nil } -func (inTx *InterceptedTransaction) checkRecursiveRelayed(userTxData []byte) error { - funcName, _, err := inTx.argsParser.ParseCallData(string(userTxData)) +func (inTx *InterceptedTransaction) checkRecursiveRelayed(userTx *transaction.Transaction) error { + if isRelayedTxV3(userTx) { + return process.ErrRecursiveRelayedTxIsNotAllowed + } + + funcName, _, err := inTx.argsParser.ParseCallData(string(userTx.Data)) if err != nil { return nil } @@ -223,6 +232,36 @@ func isRelayedTx(funcName string) bool { core.RelayedTransactionV2 == funcName } +func isRelayedTxV3(tx *transaction.Transaction) bool { + return len(tx.RelayerAddr) == len(tx.SndAddr) && len(tx.RelayerAddr) > 0 && + len(tx.RelayerSignature) == len(tx.Signature) && len(tx.RelayerSignature) > 0 +} + +func (inTx *InterceptedTransaction) verifyIfRelayedTxV3(tx *transaction.Transaction) error { + if !isRelayedTxV3(tx) { + return nil + } + + err := inTx.integrity(tx) + if err != nil { + return fmt.Errorf("inner transaction: %w", err) + } + + userTx := *tx + userTx.RelayerSignature = make([]byte, 0) // temporary removed signature for recursive relayed checks + err = inTx.verifyUserTx(&userTx) + if err != nil { + return fmt.Errorf("inner transaction: %w", err) + } + + err = inTx.verifyRelayerSig(tx) + if err != nil { + return fmt.Errorf("inner transaction: %w", err) + } + + return nil +} + func (inTx *InterceptedTransaction) verifyIfRelayedTxV2(tx *transaction.Transaction) error { funcName, userTxArgs, err := inTx.argsParser.ParseCallData(string(tx.Data)) if err != nil { @@ -272,7 +311,7 @@ func (inTx *InterceptedTransaction) verifyIfRelayedTx(tx *transaction.Transactio func (inTx *InterceptedTransaction) verifyUserTx(userTx *transaction.Transaction) error { // recursive relayed transactions are not allowed - err := inTx.checkRecursiveRelayed(userTx.Data) + err := inTx.checkRecursiveRelayed(userTx) if err != nil { return fmt.Errorf("inner transaction: %w", err) } @@ -370,6 +409,21 @@ func (inTx *InterceptedTransaction) verifySig(tx *transaction.Transaction) error return inTx.singleSigner.Verify(senderPubKey, txMessageForSigVerification, tx.Signature) } +// verifyRelayerSig checks if the tx is correctly signed by relayer +func (inTx *InterceptedTransaction) verifyRelayerSig(tx *transaction.Transaction) error { + txMessageForSigVerification, err := inTx.getTxMessageForGivenTx(tx) + if err != nil { + return err + } + + relayerPubKey, err := inTx.keyGen.PublicKeyFromByteArray(tx.RelayerAddr) + if err != nil { + return err + } + + return inTx.singleSigner.Verify(relayerPubKey, txMessageForSigVerification, tx.RelayerSignature) +} + // VerifyGuardianSig verifies if the guardian signature is valid func (inTx *InterceptedTransaction) VerifyGuardianSig(tx *transaction.Transaction) error { txMessageForSigVerification, err := inTx.getTxMessageForGivenTx(tx) diff --git a/process/transaction/interceptedTransaction_test.go b/process/transaction/interceptedTransaction_test.go index f35ce467d13..4eafa62ae8d 100644 --- a/process/transaction/interceptedTransaction_test.go +++ b/process/transaction/interceptedTransaction_test.go @@ -36,6 +36,7 @@ var senderShard = uint32(2) var recvShard = uint32(3) var senderAddress = []byte("12345678901234567890123456789012") var recvAddress = []byte("23456789012345678901234567890123") +var relayerAddress = []byte("34567890123456789012345678901234") var sigBad = []byte("bad-signature") var sigOk = []byte("signature") @@ -1500,6 +1501,46 @@ func TestInterceptedTransaction_CheckValidityOfRelayedTxV2(t *testing.T) { assert.Nil(t, err) } +func TestInterceptedTransaction_CheckValidityOfRelayedTxV3(t *testing.T) { + t.Parallel() + + minTxVersion := uint32(1) + chainID := []byte("chain") + tx := &dataTransaction.Transaction{ + Nonce: 1, + Value: big.NewInt(2), + GasLimit: 3, + GasPrice: 4, + RcvAddr: recvAddress, + SndAddr: senderAddress, + ChainID: chainID, + Version: minTxVersion, + RelayerAddr: relayerAddress, + } + txi, _ := createInterceptedTxFromPlainTxWithArgParser(tx) + err := txi.CheckValidity() + assert.Equal(t, err, process.ErrNilSignature) + + tx.Signature = sigOk + tx.RelayerSignature = sigOk + + tx.Data = []byte(core.RelayedTransactionV2 + "@" + hex.EncodeToString(recvAddress) + "@" + hex.EncodeToString(big.NewInt(0).SetUint64(0).Bytes()) + "@" + hex.EncodeToString([]byte("some method")) + "@" + hex.EncodeToString(sigOk)) + txi, _ = createInterceptedTxFromPlainTxWithArgParser(tx) + err = txi.CheckValidity() + assert.True(t, errors.Is(err, process.ErrRecursiveRelayedTxIsNotAllowed)) + + tx.Data = nil + tx.RelayerSignature = bytes.Repeat([]byte("a"), len(sigOk)) // same length but invalid relayer sig + txi, _ = createInterceptedTxFromPlainTxWithArgParser(tx) + err = txi.CheckValidity() + assert.NotNil(t, err) + + tx.RelayerSignature = sigOk + txi, _ = createInterceptedTxFromPlainTxWithArgParser(tx) + err = txi.CheckValidity() + assert.Nil(t, err) +} + // ------- IsInterfaceNil func TestInterceptedTransaction_IsInterfaceNil(t *testing.T) { t.Parallel() diff --git a/process/transaction/shardProcess.go b/process/transaction/shardProcess.go index 51910b537e2..81fd8dac766 100644 --- a/process/transaction/shardProcess.go +++ b/process/transaction/shardProcess.go @@ -240,6 +240,8 @@ func (txProc *txProcessor) ProcessTransaction(tx *transaction.Transaction) (vmco return txProc.processRelayedTx(tx, acntSnd, acntDst) case process.RelayedTxV2: return txProc.processRelayedTxV2(tx, acntSnd, acntDst) + case process.RelayedTxV3: + return txProc.processRelayedTxV3(tx) } return vmcommon.UserError, txProc.executingFailedTransaction(tx, acntSnd, process.ErrWrongTransaction) @@ -316,7 +318,7 @@ func (txProc *txProcessor) executingFailedTransaction( rpt := &receipt.Receipt{ Value: big.NewInt(0).Set(txFee), - SndAddr: tx.SndAddr, + SndAddr: acntSnd.AddressBytes(), Data: []byte(txError.Error()), TxHash: txHash, } @@ -603,8 +605,14 @@ func (txProc *txProcessor) finishExecutionOfRelayedTx( tx *transaction.Transaction, userTx *transaction.Transaction, ) (vmcommon.ReturnCode, error) { - computedFees := txProc.computeRelayedTxFees(tx, userTx) - txHash, err := txProc.processTxAtRelayer(relayerAcnt, computedFees.totalFee, computedFees.relayerFee, tx) + isUserTxOfRelayedV3 := isRelayedTxV3(tx) + relayedValue := tx.Value + if isUserTxOfRelayedV3 { + relayedValue = big.NewInt(0) + } + + computedFees := txProc.computeRelayedTxFees(tx, userTx, isUserTxOfRelayedV3) + txHash, err := txProc.processTxAtRelayer(relayerAcnt, computedFees.totalFee, computedFees.relayerFee, tx, relayedValue) if err != nil { return 0, err } @@ -613,12 +621,19 @@ func (txProc *txProcessor) finishExecutionOfRelayedTx( return vmcommon.Ok, nil } - err = txProc.addFeeAndValueToDest(acntDst, tx.Value, computedFees.remainingFee) + err = txProc.addFeeAndValueToDest(acntDst, relayedValue, computedFees.remainingFee) if err != nil { return 0, err } - return txProc.processUserTx(tx, userTx, tx.Value, tx.Nonce, txHash) + relayedNonce := tx.Nonce + relayerAddr := tx.SndAddr + if isUserTxOfRelayedV3 && !check.IfNil(relayerAcnt) { + relayedNonce = relayerAcnt.GetNonce() - 1 // nonce already increased inside processTxAtRelayer + relayerAddr = tx.RelayerAddr + } + + return txProc.processUserTx(tx, userTx, relayedValue, relayedNonce, relayerAddr, txHash) } func (txProc *txProcessor) processTxAtRelayer( @@ -626,6 +641,7 @@ func (txProc *txProcessor) processTxAtRelayer( totalFee *big.Int, relayerFee *big.Int, tx *transaction.Transaction, + valueToSubFromRelayer *big.Int, ) ([]byte, error) { txHash, err := core.CalculateHash(txProc.marshalizer, txProc.hasher, tx) if err != nil { @@ -633,7 +649,7 @@ func (txProc *txProcessor) processTxAtRelayer( } if !check.IfNil(relayerAcnt) { - err = relayerAcnt.SubFromBalance(tx.GetValue()) + err = relayerAcnt.SubFromBalance(valueToSubFromRelayer) if err != nil { return nil, err } @@ -669,6 +685,37 @@ func (txProc *txProcessor) addFeeAndValueToDest(acntDst state.UserAccountHandler return txProc.accounts.SaveAccount(acntDst) } +func (txProc *txProcessor) processRelayedTxV3(tx *transaction.Transaction) (vmcommon.ReturnCode, error) { + relayerAccount, sndAccount, err := txProc.getAccounts(tx.RelayerAddr, tx.SndAddr) + if err != nil { + return 0, err + } + + if !txProc.enableEpochsHandler.IsFlagEnabled(common.RelayedTransactionsV3Flag) { + return vmcommon.UserError, txProc.executingFailedTransaction(tx, relayerAccount, process.ErrRelayedTxV3Disabled) + } + + if !txProc.shardCoordinator.SameShard(tx.RelayerAddr, tx.SndAddr) { + return vmcommon.UserError, txProc.executingFailedTransaction(tx, relayerAccount, process.ErrShardIdMissmatch) + } + + if !check.IfNil(relayerAccount) && relayerAccount.IsGuarded() { + return vmcommon.UserError, txProc.executingFailedTransaction(tx, relayerAccount, process.ErrGuardedRelayerNotAllowed) + } + + userTx := *tx + // remove relayer addr and signature for tx type handler + userTx.RelayerAddr = nil + userTx.RelayerSignature = nil + minGasLimit := txProc.economicsFee.MinGasLimit() + userTx.GasLimit = userTx.GasLimit - minGasLimit + if userTx.GasLimit < txProc.economicsFee.ComputeGasLimit(&userTx) { + return vmcommon.UserError, txProc.executingFailedTransaction(tx, relayerAccount, process.ErrInsufficientGasLimitInTx) + } + + return txProc.finishExecutionOfRelayedTx(relayerAccount, sndAccount, tx, &userTx) +} + func (txProc *txProcessor) processRelayedTxV2( tx *transaction.Transaction, relayerAcnt, acntDst state.UserAccountHandler, @@ -736,12 +783,17 @@ func (txProc *txProcessor) processRelayedTx( return txProc.finishExecutionOfRelayedTx(relayerAcnt, acntDst, tx, userTx) } -func (txProc *txProcessor) computeRelayedTxFees(tx, userTx *transaction.Transaction) relayedFees { +func (txProc *txProcessor) computeRelayedTxFees(tx, userTx *transaction.Transaction, isUserTxOfRelayedV3 bool) relayedFees { relayerFee := txProc.economicsFee.ComputeMoveBalanceFee(tx) totalFee := txProc.economicsFee.ComputeTxFee(tx) if txProc.enableEpochsHandler.IsFlagEnabled(common.FixRelayedBaseCostFlag) { userFee := txProc.computeInnerTxFeeAfterBaseCostFix(userTx) + if isUserTxOfRelayedV3 { + relayerGas := txProc.economicsFee.MinGasLimit() + relayerFee = core.SafeMul(relayerGas, tx.GasPrice) + } + totalFee = totalFee.Add(relayerFee, userFee) } remainingFee := big.NewInt(0).Sub(totalFee, relayerFee) @@ -838,10 +890,10 @@ func (txProc *txProcessor) processUserTx( userTx *transaction.Transaction, relayedTxValue *big.Int, relayedNonce uint64, + relayerAddr []byte, originalTxHash []byte, ) (vmcommon.ReturnCode, error) { - relayerAdr := originalTx.SndAddr acntSnd, acntDst, err := txProc.getAccounts(userTx.SndAddr, userTx.RcvAddr) if err != nil { errRemove := txProc.removeValueAndConsumedFeeFromUser(userTx, relayedTxValue, originalTxHash, originalTx, err) @@ -850,7 +902,7 @@ func (txProc *txProcessor) processUserTx( } return vmcommon.UserError, txProc.executeFailedRelayedUserTx( userTx, - relayerAdr, + relayerAddr, relayedTxValue, relayedNonce, originalTx, @@ -867,7 +919,7 @@ func (txProc *txProcessor) processUserTx( } return vmcommon.UserError, txProc.executeFailedRelayedUserTx( userTx, - relayerAdr, + relayerAddr, relayedTxValue, relayedNonce, originalTx, @@ -875,7 +927,7 @@ func (txProc *txProcessor) processUserTx( err.Error()) } - scrFromTx, err := txProc.makeSCRFromUserTx(userTx, relayerAdr, relayedTxValue, originalTxHash) + scrFromTx, err := txProc.makeSCRFromUserTx(userTx, relayerAddr, relayedTxValue, originalTxHash) if err != nil { return 0, err } @@ -913,7 +965,7 @@ func (txProc *txProcessor) processUserTx( } return vmcommon.UserError, txProc.executeFailedRelayedUserTx( userTx, - relayerAdr, + relayerAddr, relayedTxValue, relayedNonce, originalTx, @@ -924,7 +976,7 @@ func (txProc *txProcessor) processUserTx( if errors.Is(err, process.ErrInvalidMetaTransaction) || errors.Is(err, process.ErrAccountNotPayable) { return vmcommon.UserError, txProc.executeFailedRelayedUserTx( userTx, - relayerAdr, + relayerAddr, relayedTxValue, relayedNonce, originalTx, diff --git a/process/transaction/shardProcess_test.go b/process/transaction/shardProcess_test.go index 88797d31a0c..b6a9814a197 100644 --- a/process/transaction/shardProcess_test.go +++ b/process/transaction/shardProcess_test.go @@ -2653,6 +2653,157 @@ func TestTxProcessor_ProcessRelayedTransactionDisabled(t *testing.T) { assert.True(t, called) } +func TestTxProcessor_ProcessRelayedTransactionV3(t *testing.T) { + t.Parallel() + + pubKeyConverter := testscommon.NewPubkeyConverterMock(4) + + userAddr := []byte("user") + tx := &transaction.Transaction{} + tx.Nonce = 0 + tx.SndAddr = []byte("sSRC") + tx.RcvAddr = userAddr + tx.Value = big.NewInt(45) + tx.GasPrice = 1 + tx.GasLimit = 1 + tx.RelayerAddr = []byte("rela") + tx.Signature = []byte("ssig") + tx.RelayerSignature = []byte("rsig") + + acntSrc := createUserAcc(tx.SndAddr) + _ = acntSrc.AddToBalance(big.NewInt(100)) + acntDst := createUserAcc(tx.RcvAddr) + _ = acntDst.AddToBalance(big.NewInt(10)) + acntRelayer := createUserAcc(tx.RelayerAddr) + _ = acntRelayer.AddToBalance(big.NewInt(10)) + + adb := &stateMock.AccountsStub{} + adb.LoadAccountCalled = func(address []byte) (vmcommon.AccountHandler, error) { + if bytes.Equal(address, tx.SndAddr) { + return acntSrc, nil + } + if bytes.Equal(address, tx.RcvAddr) { + return acntDst, nil + } + if bytes.Equal(address, tx.RelayerAddr) { + return acntRelayer, nil + } + + return nil, errors.New("failure") + } + scProcessorMock := &testscommon.SCProcessorMock{} + shardC, _ := sharding.NewMultiShardCoordinator(1, 0) + + esdtTransferParser, _ := parsers.NewESDTTransferParser(&mock.MarshalizerMock{}) + argTxTypeHandler := coordinator.ArgNewTxTypeHandler{ + PubkeyConverter: pubKeyConverter, + ShardCoordinator: shardC, + BuiltInFunctions: builtInFunctions.NewBuiltInFunctionContainer(), + ArgumentParser: parsers.NewCallArgsParser(), + ESDTTransferParser: esdtTransferParser, + EnableEpochsHandler: enableEpochsHandlerMock.NewEnableEpochsHandlerStub(common.RelayedTransactionsV3Flag), + } + txTypeHandler, _ := coordinator.NewTxTypeHandler(argTxTypeHandler) + + args := createArgsForTxProcessor() + args.Accounts = adb + args.ScProcessor = scProcessorMock + args.ShardCoordinator = shardC + args.TxTypeHandler = txTypeHandler + args.PubkeyConv = pubKeyConverter + args.ArgsParser = smartContract.NewArgumentParser() + args.EnableEpochsHandler = enableEpochsHandlerMock.NewEnableEpochsHandlerStub(common.RelayedTransactionsV3Flag) + txProc, _ := txproc.NewTxProcessor(args) + require.NotNil(t, txProc) + + t.Run("should work", func(t *testing.T) { + txCopy := *tx + returnCode, err := txProc.ProcessTransaction(&txCopy) + assert.NoError(t, err) + assert.Equal(t, vmcommon.Ok, returnCode) + }) + t.Run("getAccounts error should error", func(t *testing.T) { + tx.Nonce++ + txCopy := *tx + txCopy.RelayerAddr = []byte("newR") // same length as sender + returnCode, err := txProc.ProcessTransaction(&txCopy) + assert.Error(t, err) + assert.Equal(t, vmcommon.Ok, returnCode) + }) + t.Run("flag not active should error", func(t *testing.T) { + argsCopy := args + argsCopy.EnableEpochsHandler = enableEpochsHandlerMock.NewEnableEpochsHandlerStub() + txProcLocal, _ := txproc.NewTxProcessor(argsCopy) + require.NotNil(t, txProcLocal) + + txCopy := *tx + returnCode, err := txProcLocal.ProcessTransaction(&txCopy) + assert.Equal(t, process.ErrFailedTransaction, err) + assert.Equal(t, vmcommon.UserError, returnCode) + }) + t.Run("relayer not in the same shard with the sender should error", func(t *testing.T) { + argsCopy := args + argsCopy.ShardCoordinator = &mock.ShardCoordinatorStub{ + SameShardCalled: func(firstAddress, secondAddress []byte) bool { + return false + }, + } + txProcLocal, _ := txproc.NewTxProcessor(argsCopy) + require.NotNil(t, txProcLocal) + + txCopy := *tx + returnCode, err := txProcLocal.ProcessTransaction(&txCopy) + assert.Equal(t, process.ErrFailedTransaction, err) + assert.Equal(t, vmcommon.UserError, returnCode) + }) + t.Run("guarded relayer account should error", func(t *testing.T) { + acntRelayerCopy := acntRelayer + acntRelayerCopy.IsGuarded() + argsCopy := args + argsCopy.Accounts = &stateMock.AccountsStub{ + LoadAccountCalled: func(address []byte) (vmcommon.AccountHandler, error) { + if bytes.Equal(address, tx.SndAddr) { + return acntSrc, nil + } + if bytes.Equal(address, tx.RcvAddr) { + return acntDst, nil + } + if bytes.Equal(address, tx.RelayerAddr) { + return &stateMock.UserAccountStub{ + IsGuardedCalled: func() bool { + return true + }, + }, nil + } + + return nil, errors.New("failure") + }, + } + txProcLocal, _ := txproc.NewTxProcessor(argsCopy) + require.NotNil(t, txProcLocal) + + txCopy := *tx + returnCode, err := txProcLocal.ProcessTransaction(&txCopy) + assert.Equal(t, process.ErrFailedTransaction, err) + assert.Equal(t, vmcommon.UserError, returnCode) + }) + t.Run("insufficient gas limit should error", func(t *testing.T) { + txCopy := *tx + argsCopy := args + argsCopy.EconomicsFee = &economicsmocks.EconomicsHandlerStub{ + ComputeGasLimitCalled: func(tx data.TransactionWithFeeHandler) uint64 { + return txCopy.GasLimit + 1 + }, + } + txProcLocal, _ := txproc.NewTxProcessor(argsCopy) + require.NotNil(t, txProcLocal) + + returnCode, err := txProcLocal.ProcessTransaction(&txCopy) + assert.Equal(t, process.ErrFailedTransaction, err) + assert.Equal(t, vmcommon.UserError, returnCode) + }) +} + func TestTxProcessor_ConsumeMoveBalanceWithUserTx(t *testing.T) { t.Parallel() @@ -2761,7 +2912,7 @@ func TestTxProcessor_ProcessUserTxOfTypeRelayedShouldError(t *testing.T) { execTx, _ := txproc.NewTxProcessor(args) - returnCode, err := execTx.ProcessUserTx(&tx, &userTx, tx.Value, tx.Nonce, txHash) + returnCode, err := execTx.ProcessUserTx(&tx, &userTx, tx.Value, tx.Nonce, tx.SndAddr, txHash) assert.Nil(t, err) assert.Equal(t, vmcommon.UserError, returnCode) } @@ -2824,7 +2975,7 @@ func TestTxProcessor_ProcessUserTxOfTypeMoveBalanceShouldWork(t *testing.T) { execTx, _ := txproc.NewTxProcessor(args) - returnCode, err := execTx.ProcessUserTx(&tx, &userTx, tx.Value, tx.Nonce, txHash) + returnCode, err := execTx.ProcessUserTx(&tx, &userTx, tx.Value, tx.Nonce, tx.SndAddr, txHash) assert.Nil(t, err) assert.Equal(t, vmcommon.Ok, returnCode) } @@ -2887,7 +3038,7 @@ func TestTxProcessor_ProcessUserTxOfTypeSCDeploymentShouldWork(t *testing.T) { execTx, _ := txproc.NewTxProcessor(args) - returnCode, err := execTx.ProcessUserTx(&tx, &userTx, tx.Value, tx.Nonce, txHash) + returnCode, err := execTx.ProcessUserTx(&tx, &userTx, tx.Value, tx.Nonce, tx.SndAddr, txHash) assert.Nil(t, err) assert.Equal(t, vmcommon.Ok, returnCode) } @@ -2950,7 +3101,7 @@ func TestTxProcessor_ProcessUserTxOfTypeSCInvokingShouldWork(t *testing.T) { execTx, _ := txproc.NewTxProcessor(args) - returnCode, err := execTx.ProcessUserTx(&tx, &userTx, tx.Value, tx.Nonce, txHash) + returnCode, err := execTx.ProcessUserTx(&tx, &userTx, tx.Value, tx.Nonce, tx.SndAddr, txHash) assert.Nil(t, err) assert.Equal(t, vmcommon.Ok, returnCode) } @@ -3013,7 +3164,7 @@ func TestTxProcessor_ProcessUserTxOfTypeBuiltInFunctionCallShouldWork(t *testing execTx, _ := txproc.NewTxProcessor(args) - returnCode, err := execTx.ProcessUserTx(&tx, &userTx, tx.Value, tx.Nonce, txHash) + returnCode, err := execTx.ProcessUserTx(&tx, &userTx, tx.Value, tx.Nonce, tx.SndAddr, txHash) assert.Nil(t, err) assert.Equal(t, vmcommon.Ok, returnCode) } @@ -3080,7 +3231,7 @@ func TestTxProcessor_ProcessUserTxErrNotPayableShouldFailRelayTx(t *testing.T) { execTx, _ := txproc.NewTxProcessor(args) - returnCode, err := execTx.ProcessUserTx(&tx, &userTx, tx.Value, tx.Nonce, txHash) + returnCode, err := execTx.ProcessUserTx(&tx, &userTx, tx.Value, tx.Nonce, tx.SndAddr, txHash) assert.Nil(t, err) assert.Equal(t, vmcommon.UserError, returnCode) } @@ -3149,7 +3300,7 @@ func TestTxProcessor_ProcessUserTxFailedBuiltInFunctionCall(t *testing.T) { execTx, _ := txproc.NewTxProcessor(args) - returnCode, err := execTx.ProcessUserTx(&tx, &userTx, tx.Value, tx.Nonce, txHash) + returnCode, err := execTx.ProcessUserTx(&tx, &userTx, tx.Value, tx.Nonce, tx.SndAddr, txHash) assert.Nil(t, err) assert.Equal(t, vmcommon.ExecutionFailed, returnCode) } diff --git a/statusHandler/statusMetricsProvider.go b/statusHandler/statusMetricsProvider.go index 8050d335431..f068b2630f8 100644 --- a/statusHandler/statusMetricsProvider.go +++ b/statusHandler/statusMetricsProvider.go @@ -379,6 +379,7 @@ func (sm *statusMetrics) EnableEpochsMetrics() (map[string]interface{}, error) { enableEpochsMetrics[common.MetricCryptoOpcodesV2EnableEpoch] = sm.uint64Metrics[common.MetricCryptoOpcodesV2EnableEpoch] enableEpochsMetrics[common.MetricMultiESDTNFTTransferAndExecuteByUserEnableEpoch] = sm.uint64Metrics[common.MetricMultiESDTNFTTransferAndExecuteByUserEnableEpoch] enableEpochsMetrics[common.MetricFixRelayedMoveBalanceToNonPayableSCEnableEpoch] = sm.uint64Metrics[common.MetricFixRelayedMoveBalanceToNonPayableSCEnableEpoch] + enableEpochsMetrics[common.MetricRelayedTransactionsV3EnableEpoch] = sm.uint64Metrics[common.MetricRelayedTransactionsV3EnableEpoch] numNodesChangeConfig := sm.uint64Metrics[common.MetricMaxNodesChangeEnableEpoch+"_count"] diff --git a/statusHandler/statusMetricsProvider_test.go b/statusHandler/statusMetricsProvider_test.go index 14b5b5225d3..65374bee5f0 100644 --- a/statusHandler/statusMetricsProvider_test.go +++ b/statusHandler/statusMetricsProvider_test.go @@ -402,6 +402,7 @@ func TestStatusMetrics_EnableEpochMetrics(t *testing.T) { sm.SetUInt64Value(common.MetricCryptoOpcodesV2EnableEpoch, uint64(4)) sm.SetUInt64Value(common.MetricMultiESDTNFTTransferAndExecuteByUserEnableEpoch, uint64(4)) sm.SetUInt64Value(common.MetricFixRelayedMoveBalanceToNonPayableSCEnableEpoch, uint64(4)) + sm.SetUInt64Value(common.MetricRelayedTransactionsV3EnableEpoch, uint64(4)) maxNodesChangeConfig := []map[string]uint64{ { @@ -533,6 +534,7 @@ func TestStatusMetrics_EnableEpochMetrics(t *testing.T) { common.MetricCryptoOpcodesV2EnableEpoch: uint64(4), common.MetricMultiESDTNFTTransferAndExecuteByUserEnableEpoch: uint64(4), common.MetricFixRelayedMoveBalanceToNonPayableSCEnableEpoch: uint64(4), + common.MetricRelayedTransactionsV3EnableEpoch: uint64(4), common.MetricMaxNodesChangeEnableEpoch: []map[string]interface{}{ { From 905bd78383f2813229c46d7e8b62cf63bd6c5aee Mon Sep 17 00:00:00 2001 From: Sorin Stanculeanu Date: Tue, 29 Oct 2024 16:06:36 +0200 Subject: [PATCH 02/39] update deps after merge --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 75a2eee7f19..8a697fc5064 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( github.com/klauspost/cpuid/v2 v2.2.5 github.com/mitchellh/mapstructure v1.5.0 github.com/multiversx/mx-chain-communication-go v1.1.1-0.20241021133229-d0833256e3ec - github.com/multiversx/mx-chain-core-go v1.2.23-0.20241018134424-75bab2a9058c + github.com/multiversx/mx-chain-core-go v1.2.23-0.20241029140551-8ed69b598c83 github.com/multiversx/mx-chain-crypto-go v1.2.12 github.com/multiversx/mx-chain-es-indexer-go v1.7.10-0.20241018130218-f48c7282690b github.com/multiversx/mx-chain-logger-go v1.0.15 diff --git a/go.sum b/go.sum index 4de240abdf8..71ca81946b8 100644 --- a/go.sum +++ b/go.sum @@ -387,8 +387,8 @@ github.com/multiversx/concurrent-map v0.1.4 h1:hdnbM8VE4b0KYJaGY5yJS2aNIW9TFFsUY github.com/multiversx/concurrent-map v0.1.4/go.mod h1:8cWFRJDOrWHOTNSqgYCUvwT7c7eFQ4U2vKMOp4A/9+o= github.com/multiversx/mx-chain-communication-go v1.1.1-0.20241021133229-d0833256e3ec h1:KwpeVZXSHzic8DV9zaJZmaBgDNIIpSdbGz4Q+9fZyiI= github.com/multiversx/mx-chain-communication-go v1.1.1-0.20241021133229-d0833256e3ec/go.mod h1:WK6bP4pGEHGDDna/AYRIMtl6G9OA0NByI1Lw8PmOnRM= -github.com/multiversx/mx-chain-core-go v1.2.23-0.20241018134424-75bab2a9058c h1:hPCfMSj2vd9xNkARNxB1b3b9k8taFb+Xfja+WK97jno= -github.com/multiversx/mx-chain-core-go v1.2.23-0.20241018134424-75bab2a9058c/go.mod h1:B5zU4MFyJezmEzCsAHE9YNULmGCm2zbPHvl9hazNxmE= +github.com/multiversx/mx-chain-core-go v1.2.23-0.20241029140551-8ed69b598c83 h1:p9nCldwfWSQrvPjDQpeFwNxMxPW1NxreD967S0lT6vs= +github.com/multiversx/mx-chain-core-go v1.2.23-0.20241029140551-8ed69b598c83/go.mod h1:B5zU4MFyJezmEzCsAHE9YNULmGCm2zbPHvl9hazNxmE= github.com/multiversx/mx-chain-crypto-go v1.2.12 h1:zWip7rpUS4CGthJxfKn5MZfMfYPjVjIiCID6uX5BSOk= github.com/multiversx/mx-chain-crypto-go v1.2.12/go.mod h1:HzcPpCm1zanNct/6h2rIh+MFrlXbjA5C8+uMyXj3LI4= github.com/multiversx/mx-chain-es-indexer-go v1.7.10-0.20241018130218-f48c7282690b h1:GYvm0yGkdQ3OCfNqnyIQNzAzydN3cES8noJZ3eZHN1A= From a36e68cd9629c7aa1476fa86c72ca712c906de55 Mon Sep 17 00:00:00 2001 From: Sorin Stanculeanu Date: Tue, 29 Oct 2024 17:02:00 +0200 Subject: [PATCH 03/39] small fix + fix tests --- cmd/node/config/config.toml | 2 +- cmd/node/config/enableEpochs.toml | 4 ++-- process/transaction/baseProcess.go | 15 ++++++++++----- process/transaction/shardProcess_test.go | 13 +++++-------- 4 files changed, 18 insertions(+), 16 deletions(-) diff --git a/cmd/node/config/config.toml b/cmd/node/config/config.toml index 6e1205d5f7e..95c91cbde1b 100644 --- a/cmd/node/config/config.toml +++ b/cmd/node/config/config.toml @@ -623,7 +623,7 @@ [EpochStartConfig] GenesisEpoch = 0 MinRoundsBetweenEpochs = 20 - RoundsPerEpoch = 200 + RoundsPerEpoch = 20 # Min and Max ShuffledOutRestartThreshold represents the minimum and maximum duration of an epoch (in percentage) after a node which # has been shuffled out has to restart its process in order to start in a new shard MinShuffledOutRestartThreshold = 0.05 diff --git a/cmd/node/config/enableEpochs.toml b/cmd/node/config/enableEpochs.toml index 0b45c059ef1..74e5c734e38 100644 --- a/cmd/node/config/enableEpochs.toml +++ b/cmd/node/config/enableEpochs.toml @@ -325,7 +325,7 @@ UnJailCleanupEnableEpoch = 4 # FixRelayedBaseCostEnableEpoch represents the epoch when the fix for relayed base cost will be enabled - FixRelayedBaseCostEnableEpoch = 4 + FixRelayedBaseCostEnableEpoch = 1 # MultiESDTNFTTransferAndExecuteByUserEnableEpoch represents the epoch when enshrined sovereign cross chain opcodes are enabled MultiESDTNFTTransferAndExecuteByUserEnableEpoch = 9999999 @@ -334,7 +334,7 @@ FixRelayedMoveBalanceToNonPayableSCEnableEpoch = 4 # RelayedTransactionsV3 represents the epoch when the relayed transactions v3 will be enabled - RelayedTransactionsV3 = 5 + RelayedTransactionsV3 = 1 # BLSMultiSignerEnableEpoch represents the activation epoch for different types of BLS multi-signers BLSMultiSignerEnableEpoch = [ diff --git a/process/transaction/baseProcess.go b/process/transaction/baseProcess.go index 72ae41a3bd6..4ead73d6031 100644 --- a/process/transaction/baseProcess.go +++ b/process/transaction/baseProcess.go @@ -152,11 +152,16 @@ func (txProc *baseTxProcessor) checkTxValues( txFee = txProc.economicsFee.ComputeTxFee(tx) } - feePayer, err := txProc.getFeePayer(tx, acntSnd) + feePayer, isRelayedV3, err := txProc.getFeePayer(tx, acntSnd) if err != nil { return err } + // early exit for relayed v3, the cost will be compared with the sender balance + if isRelayedV3 { + return nil + } + if feePayer.GetBalance().Cmp(txFee) < 0 { return fmt.Errorf("%w, has: %s, wanted: %s", process.ErrInsufficientFee, @@ -182,17 +187,17 @@ func (txProc *baseTxProcessor) checkTxValues( func (txProc *baseTxProcessor) getFeePayer( tx *transaction.Transaction, acntSnd state.UserAccountHandler, -) (state.UserAccountHandler, error) { +) (state.UserAccountHandler, bool, error) { if !isRelayedTxV3(tx) { - return acntSnd, nil + return acntSnd, false, nil } acntRelayer, _, err := txProc.getAccounts(tx.RelayerAddr, tx.RelayerAddr) if err != nil { - return nil, err + return nil, true, err } - return acntRelayer, nil + return acntRelayer, true, nil } func (txProc *baseTxProcessor) computeInnerTxFee(tx *transaction.Transaction) *big.Int { diff --git a/process/transaction/shardProcess_test.go b/process/transaction/shardProcess_test.go index b6a9814a197..c8cf116fa4e 100644 --- a/process/transaction/shardProcess_test.go +++ b/process/transaction/shardProcess_test.go @@ -2718,18 +2718,11 @@ func TestTxProcessor_ProcessRelayedTransactionV3(t *testing.T) { t.Run("should work", func(t *testing.T) { txCopy := *tx + txCopy.Nonce = acntSrc.GetNonce() returnCode, err := txProc.ProcessTransaction(&txCopy) assert.NoError(t, err) assert.Equal(t, vmcommon.Ok, returnCode) }) - t.Run("getAccounts error should error", func(t *testing.T) { - tx.Nonce++ - txCopy := *tx - txCopy.RelayerAddr = []byte("newR") // same length as sender - returnCode, err := txProc.ProcessTransaction(&txCopy) - assert.Error(t, err) - assert.Equal(t, vmcommon.Ok, returnCode) - }) t.Run("flag not active should error", func(t *testing.T) { argsCopy := args argsCopy.EnableEpochsHandler = enableEpochsHandlerMock.NewEnableEpochsHandlerStub() @@ -2737,6 +2730,7 @@ func TestTxProcessor_ProcessRelayedTransactionV3(t *testing.T) { require.NotNil(t, txProcLocal) txCopy := *tx + txCopy.Nonce = acntSrc.GetNonce() returnCode, err := txProcLocal.ProcessTransaction(&txCopy) assert.Equal(t, process.ErrFailedTransaction, err) assert.Equal(t, vmcommon.UserError, returnCode) @@ -2752,6 +2746,7 @@ func TestTxProcessor_ProcessRelayedTransactionV3(t *testing.T) { require.NotNil(t, txProcLocal) txCopy := *tx + txCopy.Nonce = acntSrc.GetNonce() returnCode, err := txProcLocal.ProcessTransaction(&txCopy) assert.Equal(t, process.ErrFailedTransaction, err) assert.Equal(t, vmcommon.UserError, returnCode) @@ -2783,12 +2778,14 @@ func TestTxProcessor_ProcessRelayedTransactionV3(t *testing.T) { require.NotNil(t, txProcLocal) txCopy := *tx + txCopy.Nonce = acntSrc.GetNonce() returnCode, err := txProcLocal.ProcessTransaction(&txCopy) assert.Equal(t, process.ErrFailedTransaction, err) assert.Equal(t, vmcommon.UserError, returnCode) }) t.Run("insufficient gas limit should error", func(t *testing.T) { txCopy := *tx + txCopy.Nonce = acntSrc.GetNonce() argsCopy := args argsCopy.EconomicsFee = &economicsmocks.EconomicsHandlerStub{ ComputeGasLimitCalled: func(tx data.TransactionWithFeeHandler) uint64 { From ab06f0bca21a726a63809d5a41910fb2555e0db6 Mon Sep 17 00:00:00 2001 From: Sorin Stanculeanu Date: Wed, 30 Oct 2024 11:30:36 +0200 Subject: [PATCH 04/39] revert config values modified for local tests --- cmd/node/config/config.toml | 2 +- cmd/node/config/enableEpochs.toml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/node/config/config.toml b/cmd/node/config/config.toml index 95c91cbde1b..6e1205d5f7e 100644 --- a/cmd/node/config/config.toml +++ b/cmd/node/config/config.toml @@ -623,7 +623,7 @@ [EpochStartConfig] GenesisEpoch = 0 MinRoundsBetweenEpochs = 20 - RoundsPerEpoch = 20 + RoundsPerEpoch = 200 # Min and Max ShuffledOutRestartThreshold represents the minimum and maximum duration of an epoch (in percentage) after a node which # has been shuffled out has to restart its process in order to start in a new shard MinShuffledOutRestartThreshold = 0.05 diff --git a/cmd/node/config/enableEpochs.toml b/cmd/node/config/enableEpochs.toml index 74e5c734e38..0b45c059ef1 100644 --- a/cmd/node/config/enableEpochs.toml +++ b/cmd/node/config/enableEpochs.toml @@ -325,7 +325,7 @@ UnJailCleanupEnableEpoch = 4 # FixRelayedBaseCostEnableEpoch represents the epoch when the fix for relayed base cost will be enabled - FixRelayedBaseCostEnableEpoch = 1 + FixRelayedBaseCostEnableEpoch = 4 # MultiESDTNFTTransferAndExecuteByUserEnableEpoch represents the epoch when enshrined sovereign cross chain opcodes are enabled MultiESDTNFTTransferAndExecuteByUserEnableEpoch = 9999999 @@ -334,7 +334,7 @@ FixRelayedMoveBalanceToNonPayableSCEnableEpoch = 4 # RelayedTransactionsV3 represents the epoch when the relayed transactions v3 will be enabled - RelayedTransactionsV3 = 1 + RelayedTransactionsV3 = 5 # BLSMultiSignerEnableEpoch represents the activation epoch for different types of BLS multi-signers BLSMultiSignerEnableEpoch = [ From 09a2751f5715f799a8cb0edf9568b51d57a4ef1f Mon Sep 17 00:00:00 2001 From: Sorin Stanculeanu Date: Wed, 30 Oct 2024 14:08:54 +0200 Subject: [PATCH 05/39] guarded integration tests --- .../relayedTx/relayedTx_test.go | 69 ++++++++++++++++--- 1 file changed, 58 insertions(+), 11 deletions(-) diff --git a/integrationTests/chainSimulator/relayedTx/relayedTx_test.go b/integrationTests/chainSimulator/relayedTx/relayedTx_test.go index 7021133fddd..eabad3d708a 100644 --- a/integrationTests/chainSimulator/relayedTx/relayedTx_test.go +++ b/integrationTests/chainSimulator/relayedTx/relayedTx_test.go @@ -34,6 +34,8 @@ const ( mockRelayerTxSignature = "rsig" maxNumOfBlocksToGenerateWhenExecutingTx = 10 roundsPerEpoch = 30 + guardAccountCost = 250_000 + extraGasLimitForGuarded = minGasLimit ) var ( @@ -41,10 +43,12 @@ var ( ) func TestRelayedV3WithChainSimulator(t *testing.T) { - t.Run("successful intra shard move balance with exact gas", testRelayedV3MoveBalance(0, 0, false)) - t.Run("successful intra shard move balance with extra gas", testRelayedV3MoveBalance(0, 0, true)) - t.Run("successful cross shard move balance with exact gas", testRelayedV3MoveBalance(0, 1, false)) - t.Run("successful cross shard move balance with extra gas", testRelayedV3MoveBalance(0, 1, true)) + t.Run("successful intra shard move balance", testRelayedV3MoveBalance(0, 0, false, false)) + t.Run("successful intra shard guarded move balance", testRelayedV3MoveBalance(0, 0, false, true)) + t.Run("successful intra shard move balance with extra gas", testRelayedV3MoveBalance(0, 0, true, false)) + t.Run("successful cross shard move balance", testRelayedV3MoveBalance(0, 1, false, false)) + t.Run("successful cross shard guarded move balance", testRelayedV3MoveBalance(0, 1, false, true)) + t.Run("successful cross shard move balance with extra gas", testRelayedV3MoveBalance(0, 1, true, false)) t.Run("intra shard move balance, lower nonce", testRelayedV3MoveBalanceLowerNonce(0, 0)) t.Run("cross shard move balance, lower nonce", testRelayedV3MoveBalanceLowerNonce(0, 1)) t.Run("intra shard move balance, invalid gas", testRelayedV3MoveInvalidGasLimit(0, 0)) @@ -62,6 +66,7 @@ func testRelayedV3MoveBalance( relayerShard uint32, destinationShard uint32, extraGas bool, + guardedTx bool, ) func(t *testing.T) { return func(t *testing.T) { if testing.Short() { @@ -87,22 +92,59 @@ func testRelayedV3MoveBalance( receiver, err := cs.GenerateAndMintWalletAddress(destinationShard, big.NewInt(0)) require.NoError(t, err) + guardian, err := cs.GenerateAndMintWalletAddress(0, initialBalance) + require.NoError(t, err) + // generate one block so the minting has effect err = cs.GenerateBlocks(1) require.NoError(t, err) + senderNonce := uint64(0) + if guardedTx { + // Set guardian for sender + setGuardianTxData := "SetGuardian@" + hex.EncodeToString(guardian.Bytes) + "@" + hex.EncodeToString([]byte("uuid")) + setGuardianGasLimit := minGasLimit + 1500*len(setGuardianTxData) + guardAccountCost + setGuardianTx := generateTransaction(sender.Bytes, senderNonce, sender.Bytes, big.NewInt(0), setGuardianTxData, uint64(setGuardianGasLimit)) + _, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(setGuardianTx, maxNumOfBlocksToGenerateWhenExecutingTx) + require.NoError(t, err) + senderNonce++ + + // fast-forward until the guardian becomes active + err = cs.GenerateBlocks(roundsPerEpoch * 20) + require.NoError(t, err) + + // guard account + guardAccountTxData := "GuardAccount" + guardAccountGasLimit := minGasLimit + 1500*len(guardAccountTxData) + guardAccountCost + guardAccountTx := generateTransaction(sender.Bytes, senderNonce, sender.Bytes, big.NewInt(0), guardAccountTxData, uint64(guardAccountGasLimit)) + _, err = cs.SendTxAndGenerateBlockTilTxIsExecuted(guardAccountTx, maxNumOfBlocksToGenerateWhenExecutingTx) + require.NoError(t, err) + senderNonce++ + } + + senderBalanceBefore := getBalance(t, cs, sender) + gasLimit := minGasLimit * 2 extraGasLimit := 0 if extraGas { extraGasLimit = minGasLimit } - relayedTx := generateRelayedV3Transaction(sender.Bytes, 0, receiver.Bytes, relayer.Bytes, oneEGLD, "", uint64(gasLimit+extraGasLimit)) + if guardedTx { + gasLimit += extraGasLimitForGuarded + } + relayedTx := generateRelayedV3Transaction(sender.Bytes, senderNonce, receiver.Bytes, relayer.Bytes, oneEGLD, "", uint64(gasLimit+extraGasLimit)) + + if guardedTx { + relayedTx.GuardianAddr = guardian.Bytes + relayedTx.GuardianSignature = []byte(mockTxSignature) + relayedTx.Options = 2 + } result, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(relayedTx, maxNumOfBlocksToGenerateWhenExecutingTx) require.NoError(t, err) // check fee fields - initiallyPaidFee, fee, gasUsed := computeTxGasAndFeeBasedOnRefund(result, big.NewInt(0), true) + initiallyPaidFee, fee, gasUsed := computeTxGasAndFeeBasedOnRefund(result, big.NewInt(0), true, guardedTx) require.Equal(t, initiallyPaidFee.String(), result.InitiallyPaidFee) require.Equal(t, fee.String(), result.Fee) require.Equal(t, gasUsed, result.GasUsed) @@ -114,7 +156,7 @@ func testRelayedV3MoveBalance( // check sender balance senderBalanceAfter := getBalance(t, cs, sender) - senderBalanceDiff := big.NewInt(0).Sub(initialBalance, senderBalanceAfter) + senderBalanceDiff := big.NewInt(0).Sub(senderBalanceBefore, senderBalanceAfter) require.Equal(t, oneEGLD.String(), senderBalanceDiff.String()) // check receiver balance @@ -283,7 +325,7 @@ func testRelayedV3ScCall( require.NotZero(t, refundValue.Uint64()) // check fee fields - initiallyPaidFee, fee, gasUsed := computeTxGasAndFeeBasedOnRefund(result, refundValue, false) + initiallyPaidFee, fee, gasUsed := computeTxGasAndFeeBasedOnRefund(result, refundValue, false, false) require.Equal(t, initiallyPaidFee.String(), result.InitiallyPaidFee) require.Equal(t, fee.String(), result.Fee) require.Equal(t, gasUsed, result.GasUsed) @@ -298,7 +340,7 @@ func testRelayedV3ScCall( require.Equal(t, initialBalance.String(), senderBalanceAfter.String()) // check owner balance - _, feeDeploy, _ := computeTxGasAndFeeBasedOnRefund(resultDeploy, refundDeploy, false) + _, feeDeploy, _ := computeTxGasAndFeeBasedOnRefund(resultDeploy, refundDeploy, false, false) ownerBalanceAfter := getBalance(t, cs, owner) ownerFee := big.NewInt(0).Sub(initialBalance, ownerBalanceAfter) require.Equal(t, feeDeploy.String(), ownerFee.String()) @@ -375,7 +417,7 @@ func testRelayedV3ScCallInvalidGasLimit( require.Zero(t, refundValue.Uint64()) // check fee fields, should consume full gas - initiallyPaidFee, fee, gasUsed := computeTxGasAndFeeBasedOnRefund(result, refundValue, false) + initiallyPaidFee, fee, gasUsed := computeTxGasAndFeeBasedOnRefund(result, refundValue, false, false) require.Equal(t, initiallyPaidFee.String(), result.InitiallyPaidFee) require.Equal(t, fee.String(), result.Fee) require.Equal(t, result.InitiallyPaidFee, result.Fee) @@ -457,7 +499,7 @@ func testRelayedV3ScCallInvalidMethod( require.Zero(t, refundValue.Uint64()) // no refund, tx failed // check fee fields, should consume full gas - initiallyPaidFee, fee, _ := computeTxGasAndFeeBasedOnRefund(result, refundValue, false) + initiallyPaidFee, fee, _ := computeTxGasAndFeeBasedOnRefund(result, refundValue, false, false) require.Equal(t, initiallyPaidFee.String(), result.InitiallyPaidFee) // check relayer balance @@ -825,11 +867,16 @@ func computeTxGasAndFeeBasedOnRefund( result *transaction.ApiTransactionResult, refund *big.Int, isMoveBalance bool, + guardedTx bool, ) (*big.Int, *big.Int, uint64) { deductedGasPrice := uint64(minGasPrice / deductionFactor) initialTx := result.Tx gasForFullPrice := uint64(minGasLimit + gasPerDataByte*len(initialTx.GetData())) + if guardedTx { + gasForFullPrice += extraGasLimitForGuarded + } + if result.ProcessingTypeOnSource == process.RelayedTxV3.String() { gasForFullPrice += uint64(minGasLimit) // relayer fee } From 68dbf896b52636ecda7a6d9e6a730a0094875121 Mon Sep 17 00:00:00 2001 From: Sorin Stanculeanu Date: Mon, 11 Nov 2024 13:10:37 +0200 Subject: [PATCH 06/39] fixes after review --- common/common.go | 14 +++++++ common/common_test.go | 41 +++++++++++++++++++ .../multiShard/relayedTx/common.go | 1 - .../transactionsFeeProcessor.go | 9 +--- process/coordinator/transactionType.go | 10 +---- process/dataValidators/txValidator.go | 30 ++++++-------- process/economics/economicsData.go | 16 +++++--- process/transaction/baseProcess.go | 12 +++--- process/transaction/interceptedTransaction.go | 10 ++--- process/transaction/shardProcess.go | 16 ++++++-- process/transaction/shardProcess_test.go | 1 + 11 files changed, 103 insertions(+), 57 deletions(-) create mode 100644 common/common.go create mode 100644 common/common_test.go diff --git a/common/common.go b/common/common.go new file mode 100644 index 00000000000..9c32a9bf2e7 --- /dev/null +++ b/common/common.go @@ -0,0 +1,14 @@ +package common + +import "github.com/multiversx/mx-chain-core-go/data" + +// IsRelayedTxV3 returns true if the provided transaction is of type relayed v3 +func IsRelayedTxV3(tx data.TransactionHandler) bool { + relayedTx, isRelayedV3 := tx.(data.RelayedTransactionHandler) + if !isRelayedV3 { + return false + } + hasValidRelayer := len(relayedTx.GetRelayerAddr()) == len(tx.GetSndAddr()) && len(relayedTx.GetRelayerAddr()) > 0 + hasValidRelayerSignature := len(relayedTx.GetRelayerSignature()) == len(relayedTx.GetSignature()) && len(relayedTx.GetRelayerSignature()) > 0 + return hasValidRelayer && hasValidRelayerSignature +} diff --git a/common/common_test.go b/common/common_test.go new file mode 100644 index 00000000000..9e44ec87e9f --- /dev/null +++ b/common/common_test.go @@ -0,0 +1,41 @@ +package common + +import ( + "math/big" + "testing" + + "github.com/multiversx/mx-chain-core-go/data/smartContractResult" + "github.com/multiversx/mx-chain-core-go/data/transaction" + "github.com/stretchr/testify/require" +) + +func TestIsRelayedTxV3(t *testing.T) { + t.Parallel() + + scr := &smartContractResult.SmartContractResult{} + require.False(t, IsRelayedTxV3(scr)) + + notRelayedTxV3 := &transaction.Transaction{ + Nonce: 1, + Value: big.NewInt(100), + RcvAddr: []byte("receiver"), + SndAddr: []byte("sender0"), + GasPrice: 100, + GasLimit: 10, + Signature: []byte("signature"), + } + require.False(t, IsRelayedTxV3(notRelayedTxV3)) + + relayedTxV3 := &transaction.Transaction{ + Nonce: 1, + Value: big.NewInt(100), + RcvAddr: []byte("receiver"), + SndAddr: []byte("sender1"), + GasPrice: 100, + GasLimit: 10, + Signature: []byte("signature"), + RelayerAddr: []byte("relayer"), + RelayerSignature: []byte("signature"), + } + require.True(t, IsRelayedTxV3(relayedTxV3)) +} diff --git a/integrationTests/multiShard/relayedTx/common.go b/integrationTests/multiShard/relayedTx/common.go index b50874f8763..26fd6ea0692 100644 --- a/integrationTests/multiShard/relayedTx/common.go +++ b/integrationTests/multiShard/relayedTx/common.go @@ -262,7 +262,6 @@ func createRelayedTxV3( player.Nonce++ player.Balance.Sub(player.Balance, value) - relayer.Nonce++ txFee := economicsFee.ComputeTxFee(tx) relayer.Balance.Sub(relayer.Balance, txFee) diff --git a/outport/process/transactionsfee/transactionsFeeProcessor.go b/outport/process/transactionsfee/transactionsFeeProcessor.go index 8dfaa1bf338..4fe57119814 100644 --- a/outport/process/transactionsfee/transactionsFeeProcessor.go +++ b/outport/process/transactionsfee/transactionsFeeProcessor.go @@ -271,15 +271,8 @@ func (tep *transactionsFeeProcessor) setGasUsedAndFeeBasedOnRefundValue( refund *big.Int, epoch uint32, ) { - tx := txWithResults.GetTxHandler() - relayedTx, isRelayedV3 := tx.(data.RelayedTransactionHandler) - if isRelayedV3 { - hasValidRelayer := len(relayedTx.GetRelayerAddr()) == len(tx.GetSndAddr()) && len(relayedTx.GetRelayerAddr()) > 0 - hasValidRelayerSignature := len(relayedTx.GetRelayerSignature()) == len(relayedTx.GetSignature()) && len(relayedTx.GetRelayerSignature()) > 0 - isRelayedV3 = hasValidRelayer && hasValidRelayerSignature - } isValidUserTxAfterBaseCostActivation := !check.IfNilReflect(userTx) && tep.enableEpochsHandler.IsFlagEnabledInEpoch(common.FixRelayedBaseCostFlag, epoch) - if isValidUserTxAfterBaseCostActivation && !isRelayedV3 { + if isValidUserTxAfterBaseCostActivation && !common.IsRelayedTxV3(txWithResults.GetTxHandler()) { gasUsed, fee := tep.txFeeCalculator.ComputeGasUsedAndFeeBasedOnRefundValue(userTx, refund) tx := txWithResults.GetTxHandler() diff --git a/process/coordinator/transactionType.go b/process/coordinator/transactionType.go index dd648a4dd54..5d195c70b2c 100644 --- a/process/coordinator/transactionType.go +++ b/process/coordinator/transactionType.go @@ -83,14 +83,8 @@ func (tth *txTypeHandler) ComputeTransactionType(tx data.TransactionHandler) (pr return process.InvalidTransaction, process.InvalidTransaction } - relayedTxV3, ok := tx.(data.RelayedTransactionHandler) - if ok { - hasValidRelayer := len(relayedTxV3.GetRelayerAddr()) == len(tx.GetSndAddr()) && len(relayedTxV3.GetRelayerAddr()) > 0 - hasValidRelayerSignature := len(relayedTxV3.GetRelayerSignature()) == len(relayedTxV3.GetSignature()) && len(relayedTxV3.GetRelayerSignature()) > 0 - isRelayedV3 := hasValidRelayer && hasValidRelayerSignature - if isRelayedV3 { - return process.RelayedTxV3, process.RelayedTxV3 - } + if common.IsRelayedTxV3(tx) { + return process.RelayedTxV3, process.RelayedTxV3 } isEmptyAddress := tth.isDestAddressEmpty(tx) diff --git a/process/dataValidators/txValidator.go b/process/dataValidators/txValidator.go index b95c1bca2b7..1e0c67ee007 100644 --- a/process/dataValidators/txValidator.go +++ b/process/dataValidators/txValidator.go @@ -6,6 +6,7 @@ import ( "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-core-go/core/check" "github.com/multiversx/mx-chain-core-go/data" + "github.com/multiversx/mx-chain-go/common" "github.com/multiversx/mx-chain-go/process" "github.com/multiversx/mx-chain-go/sharding" "github.com/multiversx/mx-chain-go/state" @@ -108,24 +109,19 @@ func (txv *txValidator) getFeePayerAccount( payerAccount := accountHandler tx := interceptedTx.Transaction() - relayedTx, ok := tx.(data.RelayedTransactionHandler) - if ok { - hasValidRelayer := len(relayedTx.GetRelayerAddr()) == len(tx.GetSndAddr()) && len(relayedTx.GetRelayerAddr()) > 0 - hasValidRelayerSignature := len(relayedTx.GetRelayerSignature()) == len(relayedTx.GetSignature()) && len(relayedTx.GetRelayerSignature()) > 0 - isRelayedV3 := hasValidRelayer && hasValidRelayerSignature - if isRelayedV3 { - payerAddress = relayedTx.GetRelayerAddr() - relayerAccount, err := txv.accounts.GetExistingAccount(payerAddress) - if err != nil { - return nil, fmt.Errorf("%w for address %s and shard %d, err: %s", - process.ErrAccountNotFound, - txv.pubKeyConverter.SilentEncode(payerAddress, log), - txv.shardCoordinator.SelfId(), - err.Error(), - ) - } - payerAccount = relayerAccount + if common.IsRelayedTxV3(tx) { + relayedTx := tx.(data.RelayedTransactionHandler) + payerAddress = relayedTx.GetRelayerAddr() + relayerAccount, err := txv.accounts.GetExistingAccount(payerAddress) + if err != nil { + return nil, fmt.Errorf("%w for address %s and shard %d, err: %s", + process.ErrAccountNotFound, + txv.pubKeyConverter.SilentEncode(payerAddress, log), + txv.shardCoordinator.SelfId(), + err.Error(), + ) } + payerAccount = relayerAccount } account, ok := payerAccount.(state.UserAccountHandler) if !ok { diff --git a/process/economics/economicsData.go b/process/economics/economicsData.go index 4da39f94862..dfce9d5a6f6 100644 --- a/process/economics/economicsData.go +++ b/process/economics/economicsData.go @@ -500,12 +500,7 @@ func (ed *economicsData) ComputeGasLimitInEpoch(tx data.TransactionWithFeeHandle gasLimit += ed.getExtraGasLimitGuardedTx(epoch) } - hasValidRelayer := len(txInstance.GetRelayerAddr()) == len(txInstance.GetSndAddr()) && len(txInstance.GetRelayerAddr()) > 0 - hasValidRelayerSignature := len(txInstance.GetRelayerSignature()) == len(txInstance.GetSignature()) && len(txInstance.GetRelayerSignature()) > 0 - isRelayedV3 := hasValidRelayer && hasValidRelayerSignature - if isRelayedV3 { - gasLimit += ed.MinGasLimitInEpoch(epoch) - } + gasLimit += ed.getExtraGasLimitRelayedTx(txInstance, epoch) } return gasLimit @@ -614,6 +609,15 @@ func (ed *economicsData) ComputeGasLimitBasedOnBalanceInEpoch(tx data.Transactio return totalGasLimit, nil } +// getExtraGasLimitRelayedTx returns extra gas limit for relayed tx in a specific epoch +func (ed *economicsData) getExtraGasLimitRelayedTx(txInstance *transaction.Transaction, epoch uint32) uint64 { + if common.IsRelayedTxV3(txInstance) { + return ed.MinGasLimitInEpoch(epoch) + } + + return 0 +} + // IsInterfaceNil returns true if there is no value under the interface func (ed *economicsData) IsInterfaceNil() bool { return ed == nil diff --git a/process/transaction/baseProcess.go b/process/transaction/baseProcess.go index 4ead73d6031..d480ec22b51 100644 --- a/process/transaction/baseProcess.go +++ b/process/transaction/baseProcess.go @@ -157,11 +157,6 @@ func (txProc *baseTxProcessor) checkTxValues( return err } - // early exit for relayed v3, the cost will be compared with the sender balance - if isRelayedV3 { - return nil - } - if feePayer.GetBalance().Cmp(txFee) < 0 { return fmt.Errorf("%w, has: %s, wanted: %s", process.ErrInsufficientFee, @@ -176,6 +171,11 @@ func (txProc *baseTxProcessor) checkTxValues( txFee = core.SafeMul(tx.GasLimit, tx.GasPrice) } + // early exit for relayed v3, the cost will be compared with the sender balance + if isRelayedV3 { + return nil + } + cost := big.NewInt(0).Add(txFee, tx.Value) if feePayer.GetBalance().Cmp(cost) < 0 { return process.ErrInsufficientFunds @@ -188,7 +188,7 @@ func (txProc *baseTxProcessor) getFeePayer( tx *transaction.Transaction, acntSnd state.UserAccountHandler, ) (state.UserAccountHandler, bool, error) { - if !isRelayedTxV3(tx) { + if !common.IsRelayedTxV3(tx) { return acntSnd, false, nil } diff --git a/process/transaction/interceptedTransaction.go b/process/transaction/interceptedTransaction.go index 890939b621d..9afe05c02f3 100644 --- a/process/transaction/interceptedTransaction.go +++ b/process/transaction/interceptedTransaction.go @@ -13,6 +13,7 @@ import ( "github.com/multiversx/mx-chain-core-go/hashing" "github.com/multiversx/mx-chain-core-go/marshal" "github.com/multiversx/mx-chain-crypto-go" + "github.com/multiversx/mx-chain-go/common" "github.com/multiversx/mx-chain-go/process" "github.com/multiversx/mx-chain-go/sharding" logger "github.com/multiversx/mx-chain-logger-go" @@ -211,7 +212,7 @@ func (inTx *InterceptedTransaction) CheckValidity() error { } func (inTx *InterceptedTransaction) checkRecursiveRelayed(userTx *transaction.Transaction) error { - if isRelayedTxV3(userTx) { + if common.IsRelayedTxV3(userTx) { return process.ErrRecursiveRelayedTxIsNotAllowed } @@ -232,13 +233,8 @@ func isRelayedTx(funcName string) bool { core.RelayedTransactionV2 == funcName } -func isRelayedTxV3(tx *transaction.Transaction) bool { - return len(tx.RelayerAddr) == len(tx.SndAddr) && len(tx.RelayerAddr) > 0 && - len(tx.RelayerSignature) == len(tx.Signature) && len(tx.RelayerSignature) > 0 -} - func (inTx *InterceptedTransaction) verifyIfRelayedTxV3(tx *transaction.Transaction) error { - if !isRelayedTxV3(tx) { + if !common.IsRelayedTxV3(tx) { return nil } diff --git a/process/transaction/shardProcess.go b/process/transaction/shardProcess.go index 81fd8dac766..6637a8466d2 100644 --- a/process/transaction/shardProcess.go +++ b/process/transaction/shardProcess.go @@ -605,14 +605,20 @@ func (txProc *txProcessor) finishExecutionOfRelayedTx( tx *transaction.Transaction, userTx *transaction.Transaction, ) (vmcommon.ReturnCode, error) { - isUserTxOfRelayedV3 := isRelayedTxV3(tx) + isUserTxOfRelayedV3 := common.IsRelayedTxV3(tx) relayedValue := tx.Value if isUserTxOfRelayedV3 { relayedValue = big.NewInt(0) } computedFees := txProc.computeRelayedTxFees(tx, userTx, isUserTxOfRelayedV3) - txHash, err := txProc.processTxAtRelayer(relayerAcnt, computedFees.totalFee, computedFees.relayerFee, tx, relayedValue) + txHash, err := txProc.processTxAtRelayer( + relayerAcnt, + computedFees.totalFee, + computedFees.relayerFee, + tx, + relayedValue, + isUserTxOfRelayedV3) if err != nil { return 0, err } @@ -629,7 +635,6 @@ func (txProc *txProcessor) finishExecutionOfRelayedTx( relayedNonce := tx.Nonce relayerAddr := tx.SndAddr if isUserTxOfRelayedV3 && !check.IfNil(relayerAcnt) { - relayedNonce = relayerAcnt.GetNonce() - 1 // nonce already increased inside processTxAtRelayer relayerAddr = tx.RelayerAddr } @@ -642,6 +647,7 @@ func (txProc *txProcessor) processTxAtRelayer( relayerFee *big.Int, tx *transaction.Transaction, valueToSubFromRelayer *big.Int, + isUserTxOfRelayedV3 bool, ) ([]byte, error) { txHash, err := core.CalculateHash(txProc.marshalizer, txProc.hasher, tx) if err != nil { @@ -659,7 +665,9 @@ func (txProc *txProcessor) processTxAtRelayer( return nil, err } - relayerAcnt.IncreaseNonce(1) + if !isUserTxOfRelayedV3 { // won't increase relayer nonce for v3 + relayerAcnt.IncreaseNonce(1) + } err = txProc.accounts.SaveAccount(relayerAcnt) if err != nil { return nil, err diff --git a/process/transaction/shardProcess_test.go b/process/transaction/shardProcess_test.go index c8cf116fa4e..c278b83c80b 100644 --- a/process/transaction/shardProcess_test.go +++ b/process/transaction/shardProcess_test.go @@ -2768,6 +2768,7 @@ func TestTxProcessor_ProcessRelayedTransactionV3(t *testing.T) { IsGuardedCalled: func() bool { return true }, + Balance: big.NewInt(1), }, nil } From 531aad5fefe9ca88daf115aebc40136b44cba949 Mon Sep 17 00:00:00 2001 From: Sorin Stanculeanu Date: Mon, 11 Nov 2024 13:12:47 +0200 Subject: [PATCH 07/39] deps update --- go.mod | 6 +++--- go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 8a697fc5064..4ea01439b9c 100644 --- a/go.mod +++ b/go.mod @@ -14,10 +14,10 @@ require ( github.com/gorilla/websocket v1.5.0 github.com/klauspost/cpuid/v2 v2.2.5 github.com/mitchellh/mapstructure v1.5.0 - github.com/multiversx/mx-chain-communication-go v1.1.1-0.20241021133229-d0833256e3ec - github.com/multiversx/mx-chain-core-go v1.2.23-0.20241029140551-8ed69b598c83 + github.com/multiversx/mx-chain-communication-go v1.1.1 + github.com/multiversx/mx-chain-core-go v1.2.24-0.20241029140551-8ed69b598c83 github.com/multiversx/mx-chain-crypto-go v1.2.12 - github.com/multiversx/mx-chain-es-indexer-go v1.7.10-0.20241018130218-f48c7282690b + github.com/multiversx/mx-chain-es-indexer-go v1.7.10 github.com/multiversx/mx-chain-logger-go v1.0.15 github.com/multiversx/mx-chain-scenario-go v1.4.4 github.com/multiversx/mx-chain-storage-go v1.0.16 diff --git a/go.sum b/go.sum index 71ca81946b8..2982014a282 100644 --- a/go.sum +++ b/go.sum @@ -385,14 +385,14 @@ github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/n github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU= github.com/multiversx/concurrent-map v0.1.4 h1:hdnbM8VE4b0KYJaGY5yJS2aNIW9TFFsUYwbO0993uPI= github.com/multiversx/concurrent-map v0.1.4/go.mod h1:8cWFRJDOrWHOTNSqgYCUvwT7c7eFQ4U2vKMOp4A/9+o= -github.com/multiversx/mx-chain-communication-go v1.1.1-0.20241021133229-d0833256e3ec h1:KwpeVZXSHzic8DV9zaJZmaBgDNIIpSdbGz4Q+9fZyiI= -github.com/multiversx/mx-chain-communication-go v1.1.1-0.20241021133229-d0833256e3ec/go.mod h1:WK6bP4pGEHGDDna/AYRIMtl6G9OA0NByI1Lw8PmOnRM= -github.com/multiversx/mx-chain-core-go v1.2.23-0.20241029140551-8ed69b598c83 h1:p9nCldwfWSQrvPjDQpeFwNxMxPW1NxreD967S0lT6vs= -github.com/multiversx/mx-chain-core-go v1.2.23-0.20241029140551-8ed69b598c83/go.mod h1:B5zU4MFyJezmEzCsAHE9YNULmGCm2zbPHvl9hazNxmE= +github.com/multiversx/mx-chain-communication-go v1.1.1 h1:y4DoQeQOJTaSUsRzczQFazf8JYQmInddypApqA3AkwM= +github.com/multiversx/mx-chain-communication-go v1.1.1/go.mod h1:WK6bP4pGEHGDDna/AYRIMtl6G9OA0NByI1Lw8PmOnRM= +github.com/multiversx/mx-chain-core-go v1.2.24-0.20241029140551-8ed69b598c83 h1:VuFFYZ9hpMacAcqcKM0hg6j4D16qKAGihi3X6PaF8qs= +github.com/multiversx/mx-chain-core-go v1.2.24-0.20241029140551-8ed69b598c83/go.mod h1:B5zU4MFyJezmEzCsAHE9YNULmGCm2zbPHvl9hazNxmE= github.com/multiversx/mx-chain-crypto-go v1.2.12 h1:zWip7rpUS4CGthJxfKn5MZfMfYPjVjIiCID6uX5BSOk= github.com/multiversx/mx-chain-crypto-go v1.2.12/go.mod h1:HzcPpCm1zanNct/6h2rIh+MFrlXbjA5C8+uMyXj3LI4= -github.com/multiversx/mx-chain-es-indexer-go v1.7.10-0.20241018130218-f48c7282690b h1:GYvm0yGkdQ3OCfNqnyIQNzAzydN3cES8noJZ3eZHN1A= -github.com/multiversx/mx-chain-es-indexer-go v1.7.10-0.20241018130218-f48c7282690b/go.mod h1:oGcRK2E3Syv6vRTszWrrb/TqD8akq0yeoMr1wPPiTO4= +github.com/multiversx/mx-chain-es-indexer-go v1.7.10 h1:Umi7WN8h4BOXLw7CM3VgvaWkLGef7nXtaPIGbjBCT3U= +github.com/multiversx/mx-chain-es-indexer-go v1.7.10/go.mod h1:oGcRK2E3Syv6vRTszWrrb/TqD8akq0yeoMr1wPPiTO4= github.com/multiversx/mx-chain-logger-go v1.0.15 h1:HlNdK8etyJyL9NQ+6mIXyKPEBo+wRqOwi3n+m2QIHXc= github.com/multiversx/mx-chain-logger-go v1.0.15/go.mod h1:t3PRKaWB1M+i6gUfD27KXgzLJJC+mAQiN+FLlL1yoGQ= github.com/multiversx/mx-chain-scenario-go v1.4.4 h1:DVE2V+FPeyD/yWoC+KEfPK3jsFzHeruelESfpTlf460= From 86aaa3977c15d81bccc615ed54dec6e4399a141d Mon Sep 17 00:00:00 2001 From: axenteoctavian Date: Tue, 12 Nov 2024 18:09:11 +0200 Subject: [PATCH 08/39] implemented override with array of string and int --- common/reflectcommon/structFieldsUpdate.go | 12 ++- .../reflectcommon/structFieldsUpdate_test.go | 81 ++++++++++++++++++- testscommon/toml/config.go | 7 ++ testscommon/toml/config.toml | 4 + testscommon/toml/overwrite.toml | 79 +++++++++--------- 5 files changed, 138 insertions(+), 45 deletions(-) diff --git a/common/reflectcommon/structFieldsUpdate.go b/common/reflectcommon/structFieldsUpdate.go index be8671eff4f..4e70a0d3728 100644 --- a/common/reflectcommon/structFieldsUpdate.go +++ b/common/reflectcommon/structFieldsUpdate.go @@ -122,7 +122,7 @@ func trySetTheNewValue(value *reflect.Value, newValue interface{}) error { case reflect.Struct: structVal := reflect.ValueOf(newValue) - return trySetStructValue(value, structVal) + return trySetItemValue(value, structVal) case reflect.Map: mapValue := reflect.ValueOf(newValue) @@ -141,7 +141,7 @@ func trySetSliceValue(value *reflect.Value, newValue interface{}) error { item := sliceVal.Index(i) newItem := reflect.New(value.Type().Elem()).Elem() - err := trySetStructValue(&newItem, item) + err := trySetItemValue(&newItem, item) if err != nil { return err } @@ -154,7 +154,7 @@ func trySetSliceValue(value *reflect.Value, newValue interface{}) error { return nil } -func trySetStructValue(value *reflect.Value, newValue reflect.Value) error { +func trySetItemValue(value *reflect.Value, newValue reflect.Value) error { switch newValue.Kind() { case reflect.Invalid: return fmt.Errorf("invalid new value kind") @@ -162,6 +162,12 @@ func trySetStructValue(value *reflect.Value, newValue reflect.Value) error { return updateStructFromMap(value, newValue) case reflect.Struct: // overwrite with go struct return updateStructFromStruct(value, newValue) + case reflect.Interface: + return trySetTheNewValue(value, newValue.Interface()) + case reflect.String: + return trySetTheNewValue(value, newValue.Interface()) + case reflect.Int: + return trySetTheNewValue(value, newValue.Interface()) default: return fmt.Errorf("unsupported type <%s> when trying to set the value of type <%s>", newValue.Kind(), value.Kind()) } diff --git a/common/reflectcommon/structFieldsUpdate_test.go b/common/reflectcommon/structFieldsUpdate_test.go index 44d3ae7d694..bcf168a25b3 100644 --- a/common/reflectcommon/structFieldsUpdate_test.go +++ b/common/reflectcommon/structFieldsUpdate_test.go @@ -76,12 +76,12 @@ func TestAdaptStructureValueBasedOnPath(t *testing.T) { t.Parallel() path := "TrieSyncStorage.DB" - expectedNewValue := "provided value" + expectedNewValue := false cfg := &config.Config{} err := AdaptStructureValueBasedOnPath(cfg, path, expectedNewValue) - require.Equal(t, "unsupported type when trying to set the value of type ", err.Error()) + require.Equal(t, "unsupported type when trying to set the value of type ", err.Error()) }) t.Run("should error when setting invalid type on struct", func(t *testing.T) { @@ -1064,10 +1064,10 @@ func TestAdaptStructureValueBasedOnPath(t *testing.T) { path := "TestConfigNestedStruct.ConfigNestedStruct.Message.MessageDescription" - expectedNewValue := []int{10, 20} + expectedNewValue := []float32{10.1, 20.2} err = AdaptStructureValueBasedOnPath(testConfig, path, expectedNewValue) - require.Equal(t, "unsupported type when trying to set the value of type ", err.Error()) + require.Equal(t, "unsupported type when trying to set the value of type ", err.Error()) }) t.Run("should error on slice when override different struct", func(t *testing.T) { @@ -1204,6 +1204,79 @@ func TestAdaptStructureValueBasedOnPath(t *testing.T) { require.Equal(t, "unsupported type when trying to add value in type ", err.Error()) }) + t.Run("should work and override string array from config", func(t *testing.T) { + t.Parallel() + + testConfig, err := loadTestConfig("../../testscommon/toml/config.toml") + require.NoError(t, err) + + expectedArray := []string{"x", "y", "z"} + + err = AdaptStructureValueBasedOnPath(testConfig, "TestArray.Strings", expectedArray) + require.NoError(t, err) + require.Equal(t, expectedArray, testConfig.TestArray.Strings) + }) + + t.Run("should work and override int array from config", func(t *testing.T) { + t.Parallel() + + testConfig, err := loadTestConfig("../../testscommon/toml/config.toml") + require.NoError(t, err) + + expectedArray := []int{10, 20, 30} + + err = AdaptStructureValueBasedOnPath(testConfig, "TestArray.Ints", expectedArray) + require.NoError(t, err) + require.Equal(t, expectedArray, testConfig.TestArray.Ints) + }) + + t.Run("should work and override string array", func(t *testing.T) { + t.Parallel() + + testConfig, err := loadTestConfig("../../testscommon/toml/config.toml") + require.NoError(t, err) + + overrideConfig, err := loadOverrideConfig("../../testscommon/toml/overwrite.toml") + require.NoError(t, err) + + err = AdaptStructureValueBasedOnPath(testConfig, overrideConfig.OverridableConfigTomlValues[38].Path, overrideConfig.OverridableConfigTomlValues[38].Value) + require.NoError(t, err) + expectedArray := []string{"x", "y", "z"} + require.Equal(t, expectedArray, testConfig.TestArray.Strings) + }) + + t.Run("should work and override int array", func(t *testing.T) { + t.Parallel() + + testConfig, err := loadTestConfig("../../testscommon/toml/config.toml") + require.NoError(t, err) + + overrideConfig, err := loadOverrideConfig("../../testscommon/toml/overwrite.toml") + require.NoError(t, err) + + err = AdaptStructureValueBasedOnPath(testConfig, overrideConfig.OverridableConfigTomlValues[39].Path, overrideConfig.OverridableConfigTomlValues[39].Value) + require.NoError(t, err) + expectedArray := []int{10, 20, 30} + require.Equal(t, expectedArray, testConfig.TestArray.Ints) + }) + + t.Run("should work and override struct of array", func(t *testing.T) { + t.Parallel() + + testConfig, err := loadTestConfig("../../testscommon/toml/config.toml") + require.NoError(t, err) + + overrideConfig, err := loadOverrideConfig("../../testscommon/toml/overwrite.toml") + require.NoError(t, err) + expectedStringsArray := []string{"x", "y", "z"} + expectedIntsArray := []int{10, 20, 30} + + err = AdaptStructureValueBasedOnPath(testConfig, overrideConfig.OverridableConfigTomlValues[40].Path, overrideConfig.OverridableConfigTomlValues[40].Value) + require.NoError(t, err) + require.Equal(t, expectedStringsArray, testConfig.TestArray.Strings) + require.Equal(t, expectedIntsArray, testConfig.TestArray.Ints) + }) + } func loadTestConfig(filepath string) (*toml.Config, error) { diff --git a/testscommon/toml/config.go b/testscommon/toml/config.go index 56cfeb1f0ad..7f64ee446d4 100644 --- a/testscommon/toml/config.go +++ b/testscommon/toml/config.go @@ -16,6 +16,7 @@ type Config struct { TestConfigNestedStruct TestMap TestInterface + TestArray } // TestConfigI8 will hold an int8 value for testing @@ -180,3 +181,9 @@ type MapValues struct { type TestInterface struct { Value interface{} } + +// TestArray will hold an array of strings and integers +type TestArray struct { + Strings []string + Ints []int +} diff --git a/testscommon/toml/config.toml b/testscommon/toml/config.toml index 91512d5e664..890e3922789 100644 --- a/testscommon/toml/config.toml +++ b/testscommon/toml/config.toml @@ -51,3 +51,7 @@ [Map] [Map.Key1] Number = 999 + +[TestArray] + Strings = ["a", "b", "c"] + Ints = [0, 1, 2] diff --git a/testscommon/toml/overwrite.toml b/testscommon/toml/overwrite.toml index 63f74b7828c..5e495e5c08b 100644 --- a/testscommon/toml/overwrite.toml +++ b/testscommon/toml/overwrite.toml @@ -1,40 +1,43 @@ OverridableConfigTomlValues = [ - { File = "config.toml", Path = "TestConfigI8.Int8.Number", Value = 127 }, - { File = "config.toml", Path = "TestConfigI8.Int8.Number", Value = 128 }, - { File = "config.toml", Path = "TestConfigI8.Int8.Number", Value = -128 }, - { File = "config.toml", Path = "TestConfigI8.Int8.Number", Value = -129 }, - { File = "config.toml", Path = "TestConfigI16.Int16.Number", Value = 32767 }, - { File = "config.toml", Path = "TestConfigI16.Int16.Number", Value = 32768 }, - { File = "config.toml", Path = "TestConfigI16.Int16.Number", Value = -32768 }, - { File = "config.toml", Path = "TestConfigI16.Int16.Number", Value = -32769 }, - { File = "config.toml", Path = "TestConfigI32.Int32.Number", Value = 2147483647 }, - { File = "config.toml", Path = "TestConfigI32.Int32.Number", Value = 2147483648 }, - { File = "config.toml", Path = "TestConfigI32.Int32.Number", Value = -2147483648 }, - { File = "config.toml", Path = "TestConfigI32.Int32.Number", Value = -2147483649 }, - { File = "config.toml", Path = "TestConfigI64.Int64.Number", Value = 9223372036854775807 }, - { File = "config.toml", Path = "TestConfigI64.Int64.Number", Value = -9223372036854775808 }, - { File = "config.toml", Path = "TestConfigU8.Uint8.Number", Value = 255 }, - { File = "config.toml", Path = "TestConfigU8.Uint8.Number", Value = 256 }, - { File = "config.toml", Path = "TestConfigU8.Uint8.Number", Value = -256 }, - { File = "config.toml", Path = "TestConfigU16.Uint16.Number", Value = 65535 }, - { File = "config.toml", Path = "TestConfigU16.Uint16.Number", Value = 65536 }, - { File = "config.toml", Path = "TestConfigU16.Uint16.Number", Value = -65536 }, - { File = "config.toml", Path = "TestConfigU32.Uint32.Number", Value = 4294967295 }, - { File = "config.toml", Path = "TestConfigU32.Uint32.Number", Value = 4294967296 }, - { File = "config.toml", Path = "TestConfigU32.Uint32.Number", Value = -4294967296 }, - { File = "config.toml", Path = "TestConfigU64.Uint64.Number", Value = 9223372036854775807 }, - { File = "config.toml", Path = "TestConfigU64.Uint64.Number", Value = -9223372036854775808 }, - { File = "config.toml", Path = "TestConfigF32.Float32.Number", Value = 3.4 }, - { File = "config.toml", Path = "TestConfigF32.Float32.Number", Value = 3.4e+39 }, - { File = "config.toml", Path = "TestConfigF32.Float32.Number", Value = -3.4 }, - { File = "config.toml", Path = "TestConfigF32.Float32.Number", Value = -3.4e+40 }, - { File = "config.toml", Path = "TestConfigF64.Float64.Number", Value = 1.7e+308 }, - { File = "config.toml", Path = "TestConfigF64.Float64.Number", Value = -1.7e+308 }, - { File = "config.toml", Path = "TestConfigStruct.ConfigStruct.Description", Value = { Number = 11 } }, - { File = "config.toml", Path = "TestConfigStruct.ConfigStruct.Description", Value = { Nr = 222 } }, - { File = "config.toml", Path = "TestConfigStruct.ConfigStruct.Description", Value = { Number = "11" } }, - { File = "config.toml", Path = "TestConfigNestedStruct.ConfigNestedStruct", Value = { Text = "Overwritten text", Message = { Public = false, MessageDescription = [{ Text = "Overwritten Text1" }] } } }, - { File = "config.toml", Path = "TestConfigNestedStruct.ConfigNestedStruct.Message.MessageDescription", Value = [{ Text = "Overwritten Text1" }, { Text = "Overwritten Text2" }] }, - { File = "config.toml", Path = "TestMap.Map", Value = { "Key1" = { Number = 10 }, "Key2" = { Number = 11 } } }, - { File = "config.toml", Path = "TestMap.Map", Value = { "Key2" = { Number = 2 }, "Key3" = { Number = 3 } } }, + { File = "config.toml", Path = "TestConfigI8.Int8.Number", Value = 127 }, + { File = "config.toml", Path = "TestConfigI8.Int8.Number", Value = 128 }, + { File = "config.toml", Path = "TestConfigI8.Int8.Number", Value = -128 }, + { File = "config.toml", Path = "TestConfigI8.Int8.Number", Value = -129 }, + { File = "config.toml", Path = "TestConfigI16.Int16.Number", Value = 32767 }, + { File = "config.toml", Path = "TestConfigI16.Int16.Number", Value = 32768 }, + { File = "config.toml", Path = "TestConfigI16.Int16.Number", Value = -32768 }, + { File = "config.toml", Path = "TestConfigI16.Int16.Number", Value = -32769 }, + { File = "config.toml", Path = "TestConfigI32.Int32.Number", Value = 2147483647 }, + { File = "config.toml", Path = "TestConfigI32.Int32.Number", Value = 2147483648 }, + { File = "config.toml", Path = "TestConfigI32.Int32.Number", Value = -2147483648 }, + { File = "config.toml", Path = "TestConfigI32.Int32.Number", Value = -2147483649 }, + { File = "config.toml", Path = "TestConfigI64.Int64.Number", Value = 9223372036854775807 }, + { File = "config.toml", Path = "TestConfigI64.Int64.Number", Value = -9223372036854775808 }, + { File = "config.toml", Path = "TestConfigU8.Uint8.Number", Value = 255 }, + { File = "config.toml", Path = "TestConfigU8.Uint8.Number", Value = 256 }, + { File = "config.toml", Path = "TestConfigU8.Uint8.Number", Value = -256 }, + { File = "config.toml", Path = "TestConfigU16.Uint16.Number", Value = 65535 }, + { File = "config.toml", Path = "TestConfigU16.Uint16.Number", Value = 65536 }, + { File = "config.toml", Path = "TestConfigU16.Uint16.Number", Value = -65536 }, + { File = "config.toml", Path = "TestConfigU32.Uint32.Number", Value = 4294967295 }, + { File = "config.toml", Path = "TestConfigU32.Uint32.Number", Value = 4294967296 }, + { File = "config.toml", Path = "TestConfigU32.Uint32.Number", Value = -4294967296 }, + { File = "config.toml", Path = "TestConfigU64.Uint64.Number", Value = 9223372036854775807 }, + { File = "config.toml", Path = "TestConfigU64.Uint64.Number", Value = -9223372036854775808 }, + { File = "config.toml", Path = "TestConfigF32.Float32.Number", Value = 3.4 }, + { File = "config.toml", Path = "TestConfigF32.Float32.Number", Value = 3.4e+39 }, + { File = "config.toml", Path = "TestConfigF32.Float32.Number", Value = -3.4 }, + { File = "config.toml", Path = "TestConfigF32.Float32.Number", Value = -3.4e+40 }, + { File = "config.toml", Path = "TestConfigF64.Float64.Number", Value = 1.7e+308 }, + { File = "config.toml", Path = "TestConfigF64.Float64.Number", Value = -1.7e+308 }, + { File = "config.toml", Path = "TestConfigStruct.ConfigStruct.Description", Value = { Number = 11 } }, + { File = "config.toml", Path = "TestConfigStruct.ConfigStruct.Description", Value = { Nr = 222 } }, + { File = "config.toml", Path = "TestConfigStruct.ConfigStruct.Description", Value = { Number = "11" } }, + { File = "config.toml", Path = "TestConfigNestedStruct.ConfigNestedStruct", Value = { Text = "Overwritten text", Message = { Public = false, MessageDescription = [{ Text = "Overwritten Text1" }] } } }, + { File = "config.toml", Path = "TestConfigNestedStruct.ConfigNestedStruct.Message.MessageDescription", Value = [{ Text = "Overwritten Text1" }, { Text = "Overwritten Text2" }] }, + { File = "config.toml", Path = "TestMap.Map", Value = { "Key1" = { Number = 10 }, "Key2" = { Number = 11 } } }, + { File = "config.toml", Path = "TestMap.Map", Value = { "Key2" = { Number = 2 }, "Key3" = { Number = 3 } } }, + { File = "config.toml", Path = "TestArray.Strings", Value = ["x", "y", "z"] }, + { File = "config.toml", Path = "TestArray.Ints", Value = [10, 20, 30] }, + { File = "config.toml", Path = "TestArray", Value = { Strings = ["x", "y", "z"], Ints = [10, 20, 30] } }, ] From 1a05453a36674c2eb21e47ecc70a626fc6054b84 Mon Sep 17 00:00:00 2001 From: axenteoctavian Date: Tue, 12 Nov 2024 18:23:38 +0200 Subject: [PATCH 09/39] better way for set slice --- common/reflectcommon/structFieldsUpdate.go | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/common/reflectcommon/structFieldsUpdate.go b/common/reflectcommon/structFieldsUpdate.go index 4e70a0d3728..7f8940b0400 100644 --- a/common/reflectcommon/structFieldsUpdate.go +++ b/common/reflectcommon/structFieldsUpdate.go @@ -122,11 +122,11 @@ func trySetTheNewValue(value *reflect.Value, newValue interface{}) error { case reflect.Struct: structVal := reflect.ValueOf(newValue) - return trySetItemValue(value, structVal) + return trySetStructValue(value, structVal) case reflect.Map: mapValue := reflect.ValueOf(newValue) - return tryUpdateMapValue(value, mapValue) + return trySetMapValue(value, mapValue) default: return fmt.Errorf("unsupported type <%s> when trying to set the value '%v' of type <%s>", valueKind, newValue, reflect.TypeOf(newValue)) } @@ -141,7 +141,7 @@ func trySetSliceValue(value *reflect.Value, newValue interface{}) error { item := sliceVal.Index(i) newItem := reflect.New(value.Type().Elem()).Elem() - err := trySetItemValue(&newItem, item) + err := trySetTheNewValue(&newItem, item.Interface()) if err != nil { return err } @@ -154,7 +154,7 @@ func trySetSliceValue(value *reflect.Value, newValue interface{}) error { return nil } -func trySetItemValue(value *reflect.Value, newValue reflect.Value) error { +func trySetStructValue(value *reflect.Value, newValue reflect.Value) error { switch newValue.Kind() { case reflect.Invalid: return fmt.Errorf("invalid new value kind") @@ -162,18 +162,12 @@ func trySetItemValue(value *reflect.Value, newValue reflect.Value) error { return updateStructFromMap(value, newValue) case reflect.Struct: // overwrite with go struct return updateStructFromStruct(value, newValue) - case reflect.Interface: - return trySetTheNewValue(value, newValue.Interface()) - case reflect.String: - return trySetTheNewValue(value, newValue.Interface()) - case reflect.Int: - return trySetTheNewValue(value, newValue.Interface()) default: return fmt.Errorf("unsupported type <%s> when trying to set the value of type <%s>", newValue.Kind(), value.Kind()) } } -func tryUpdateMapValue(value *reflect.Value, newValue reflect.Value) error { +func trySetMapValue(value *reflect.Value, newValue reflect.Value) error { if value.IsNil() { value.Set(reflect.MakeMap(value.Type())) } From 458e04fae54140f6b46d27fa6a34e27ea540f89f Mon Sep 17 00:00:00 2001 From: axenteoctavian Date: Tue, 12 Nov 2024 18:25:38 +0200 Subject: [PATCH 10/39] rollback some changes --- common/reflectcommon/structFieldsUpdate_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/common/reflectcommon/structFieldsUpdate_test.go b/common/reflectcommon/structFieldsUpdate_test.go index bcf168a25b3..79bf0caacb3 100644 --- a/common/reflectcommon/structFieldsUpdate_test.go +++ b/common/reflectcommon/structFieldsUpdate_test.go @@ -76,12 +76,12 @@ func TestAdaptStructureValueBasedOnPath(t *testing.T) { t.Parallel() path := "TrieSyncStorage.DB" - expectedNewValue := false + expectedNewValue := "provided value" cfg := &config.Config{} err := AdaptStructureValueBasedOnPath(cfg, path, expectedNewValue) - require.Equal(t, "unsupported type when trying to set the value of type ", err.Error()) + require.Equal(t, "unsupported type when trying to set the value of type ", err.Error()) }) t.Run("should error when setting invalid type on struct", func(t *testing.T) { @@ -1064,10 +1064,10 @@ func TestAdaptStructureValueBasedOnPath(t *testing.T) { path := "TestConfigNestedStruct.ConfigNestedStruct.Message.MessageDescription" - expectedNewValue := []float32{10.1, 20.2} + expectedNewValue := []int{10, 20} err = AdaptStructureValueBasedOnPath(testConfig, path, expectedNewValue) - require.Equal(t, "unsupported type when trying to set the value of type ", err.Error()) + require.Equal(t, "unsupported type when trying to set the value of type ", err.Error()) }) t.Run("should error on slice when override different struct", func(t *testing.T) { From 8a1e9ed20c1d980f1602d8e12af7407aebf41bf6 Mon Sep 17 00:00:00 2001 From: axenteoctavian Date: Tue, 12 Nov 2024 18:36:24 +0200 Subject: [PATCH 11/39] update test name --- common/reflectcommon/structFieldsUpdate_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/reflectcommon/structFieldsUpdate_test.go b/common/reflectcommon/structFieldsUpdate_test.go index 79bf0caacb3..c2b3edced1f 100644 --- a/common/reflectcommon/structFieldsUpdate_test.go +++ b/common/reflectcommon/structFieldsUpdate_test.go @@ -1260,7 +1260,7 @@ func TestAdaptStructureValueBasedOnPath(t *testing.T) { require.Equal(t, expectedArray, testConfig.TestArray.Ints) }) - t.Run("should work and override struct of array", func(t *testing.T) { + t.Run("should work and override struct of arrays", func(t *testing.T) { t.Parallel() testConfig, err := loadTestConfig("../../testscommon/toml/config.toml") From 0fd6cedc626774315657a74fdc621148027086cb Mon Sep 17 00:00:00 2001 From: axenteoctavian Date: Wed, 13 Nov 2024 10:02:24 +0200 Subject: [PATCH 12/39] update test name --- common/reflectcommon/structFieldsUpdate_test.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/common/reflectcommon/structFieldsUpdate_test.go b/common/reflectcommon/structFieldsUpdate_test.go index c2b3edced1f..27a30ea9d00 100644 --- a/common/reflectcommon/structFieldsUpdate_test.go +++ b/common/reflectcommon/structFieldsUpdate_test.go @@ -1204,7 +1204,7 @@ func TestAdaptStructureValueBasedOnPath(t *testing.T) { require.Equal(t, "unsupported type when trying to add value in type ", err.Error()) }) - t.Run("should work and override string array from config", func(t *testing.T) { + t.Run("should work and override string array", func(t *testing.T) { t.Parallel() testConfig, err := loadTestConfig("../../testscommon/toml/config.toml") @@ -1217,7 +1217,7 @@ func TestAdaptStructureValueBasedOnPath(t *testing.T) { require.Equal(t, expectedArray, testConfig.TestArray.Strings) }) - t.Run("should work and override int array from config", func(t *testing.T) { + t.Run("should work and override int array", func(t *testing.T) { t.Parallel() testConfig, err := loadTestConfig("../../testscommon/toml/config.toml") @@ -1230,7 +1230,7 @@ func TestAdaptStructureValueBasedOnPath(t *testing.T) { require.Equal(t, expectedArray, testConfig.TestArray.Ints) }) - t.Run("should work and override string array", func(t *testing.T) { + t.Run("should work and override string array from toml", func(t *testing.T) { t.Parallel() testConfig, err := loadTestConfig("../../testscommon/toml/config.toml") @@ -1245,7 +1245,7 @@ func TestAdaptStructureValueBasedOnPath(t *testing.T) { require.Equal(t, expectedArray, testConfig.TestArray.Strings) }) - t.Run("should work and override int array", func(t *testing.T) { + t.Run("should work and override int array from toml", func(t *testing.T) { t.Parallel() testConfig, err := loadTestConfig("../../testscommon/toml/config.toml") @@ -1260,7 +1260,7 @@ func TestAdaptStructureValueBasedOnPath(t *testing.T) { require.Equal(t, expectedArray, testConfig.TestArray.Ints) }) - t.Run("should work and override struct of arrays", func(t *testing.T) { + t.Run("should work and override struct of arrays from toml", func(t *testing.T) { t.Parallel() testConfig, err := loadTestConfig("../../testscommon/toml/config.toml") From 6c02c942752af944fe5d97363be7139e05e61a28 Mon Sep 17 00:00:00 2001 From: Sorin Stanculeanu Date: Wed, 13 Nov 2024 17:44:24 +0200 Subject: [PATCH 13/39] further fixes after review --- common/common.go | 4 +- common/common_test.go | 6 +- .../multiShard/relayedTx/common.go | 12 +- .../transactionsFeeProcessor.go | 4 +- process/coordinator/transactionType.go | 2 +- process/dataValidators/txValidator.go | 2 +- process/economics/economicsData.go | 2 +- process/transaction/baseProcess.go | 61 +++- process/transaction/interceptedTransaction.go | 4 +- process/transaction/shardProcess.go | 267 +++++++++++++++--- 10 files changed, 306 insertions(+), 58 deletions(-) diff --git a/common/common.go b/common/common.go index 9c32a9bf2e7..136c449d87d 100644 --- a/common/common.go +++ b/common/common.go @@ -2,8 +2,8 @@ package common import "github.com/multiversx/mx-chain-core-go/data" -// IsRelayedTxV3 returns true if the provided transaction is of type relayed v3 -func IsRelayedTxV3(tx data.TransactionHandler) bool { +// IsValidRelayedTxV3 returns true if the provided transaction a valid transaction of type relayed v3 +func IsValidRelayedTxV3(tx data.TransactionHandler) bool { relayedTx, isRelayedV3 := tx.(data.RelayedTransactionHandler) if !isRelayedV3 { return false diff --git a/common/common_test.go b/common/common_test.go index 9e44ec87e9f..1786a9d9421 100644 --- a/common/common_test.go +++ b/common/common_test.go @@ -13,7 +13,7 @@ func TestIsRelayedTxV3(t *testing.T) { t.Parallel() scr := &smartContractResult.SmartContractResult{} - require.False(t, IsRelayedTxV3(scr)) + require.False(t, IsValidRelayedTxV3(scr)) notRelayedTxV3 := &transaction.Transaction{ Nonce: 1, @@ -24,7 +24,7 @@ func TestIsRelayedTxV3(t *testing.T) { GasLimit: 10, Signature: []byte("signature"), } - require.False(t, IsRelayedTxV3(notRelayedTxV3)) + require.False(t, IsValidRelayedTxV3(notRelayedTxV3)) relayedTxV3 := &transaction.Transaction{ Nonce: 1, @@ -37,5 +37,5 @@ func TestIsRelayedTxV3(t *testing.T) { RelayerAddr: []byte("relayer"), RelayerSignature: []byte("signature"), } - require.True(t, IsRelayedTxV3(relayedTxV3)) + require.True(t, IsValidRelayedTxV3(relayedTxV3)) } diff --git a/integrationTests/multiShard/relayedTx/common.go b/integrationTests/multiShard/relayedTx/common.go index 26fd6ea0692..c440b574d8c 100644 --- a/integrationTests/multiShard/relayedTx/common.go +++ b/integrationTests/multiShard/relayedTx/common.go @@ -2,7 +2,6 @@ package relayedTx import ( "encoding/hex" - "fmt" "math/big" "github.com/multiversx/mx-chain-core-go/core" @@ -12,8 +11,11 @@ import ( "github.com/multiversx/mx-chain-go/integrationTests" "github.com/multiversx/mx-chain-go/process" "github.com/multiversx/mx-chain-go/state" + logger "github.com/multiversx/mx-chain-logger-go" ) +var log = logger.GetOrCreate("relayedtests") + // CreateGeneralSetupForRelayTxTest will create the general setup for relayed transactions func CreateGeneralSetupForRelayTxTest(baseCostFixEnabled bool) ([]*integrationTests.TestProcessorNode, []int, []*integrationTests.TestWalletAccount, *integrationTests.TestWalletAccount) { initialVal := big.NewInt(10000000000) @@ -94,7 +96,7 @@ func CreateAndSendRelayedAndUserTx( _, err := txDispatcherNode.SendTransaction(relayedTx) if err != nil { - fmt.Println(err.Error()) + log.Error("CreateAndSendRelayedAndUserTx.SendTransaction", "error", err) } return relayedTx, userTx @@ -117,7 +119,7 @@ func CreateAndSendRelayedAndUserTxV2( _, err := txDispatcherNode.SendTransaction(relayedTx) if err != nil { - fmt.Println(err.Error()) + log.Error("CreateAndSendRelayedAndUserTxV2.SendTransaction", "error", err) } return relayedTx, userTx @@ -139,7 +141,7 @@ func CreateAndSendRelayedAndUserTxV3( _, err := txDispatcherNode.SendTransaction(relayedTx) if err != nil { - fmt.Println(err.Error()) + log.Error("CreateAndSendRelayedAndUserTxV3.SendTransaction", "error", err) } return relayedTx, relayedTx @@ -281,7 +283,7 @@ func createAndSendSimpleTransaction( userTx := createUserTx(player, rcvAddr, value, gasLimit, txData) _, err := txDispatcherNode.SendTransaction(userTx) if err != nil { - fmt.Println(err.Error()) + log.Error("createAndSendSimpleTransaction.SendTransaction", "error", err) } } diff --git a/outport/process/transactionsfee/transactionsFeeProcessor.go b/outport/process/transactionsfee/transactionsFeeProcessor.go index 4fe57119814..bbe1979b1b6 100644 --- a/outport/process/transactionsfee/transactionsFeeProcessor.go +++ b/outport/process/transactionsfee/transactionsFeeProcessor.go @@ -271,8 +271,8 @@ func (tep *transactionsFeeProcessor) setGasUsedAndFeeBasedOnRefundValue( refund *big.Int, epoch uint32, ) { - isValidUserTxAfterBaseCostActivation := !check.IfNilReflect(userTx) && tep.enableEpochsHandler.IsFlagEnabledInEpoch(common.FixRelayedBaseCostFlag, epoch) - if isValidUserTxAfterBaseCostActivation && !common.IsRelayedTxV3(txWithResults.GetTxHandler()) { + isValidUserTxAfterBaseCostActivation := !check.IfNil(userTx) && tep.enableEpochsHandler.IsFlagEnabledInEpoch(common.FixRelayedBaseCostFlag, epoch) + if isValidUserTxAfterBaseCostActivation && !common.IsValidRelayedTxV3(txWithResults.GetTxHandler()) { gasUsed, fee := tep.txFeeCalculator.ComputeGasUsedAndFeeBasedOnRefundValue(userTx, refund) tx := txWithResults.GetTxHandler() diff --git a/process/coordinator/transactionType.go b/process/coordinator/transactionType.go index 5d195c70b2c..77de8a5476a 100644 --- a/process/coordinator/transactionType.go +++ b/process/coordinator/transactionType.go @@ -83,7 +83,7 @@ func (tth *txTypeHandler) ComputeTransactionType(tx data.TransactionHandler) (pr return process.InvalidTransaction, process.InvalidTransaction } - if common.IsRelayedTxV3(tx) { + if common.IsValidRelayedTxV3(tx) { return process.RelayedTxV3, process.RelayedTxV3 } diff --git a/process/dataValidators/txValidator.go b/process/dataValidators/txValidator.go index 1e0c67ee007..414ce52c5b6 100644 --- a/process/dataValidators/txValidator.go +++ b/process/dataValidators/txValidator.go @@ -109,7 +109,7 @@ func (txv *txValidator) getFeePayerAccount( payerAccount := accountHandler tx := interceptedTx.Transaction() - if common.IsRelayedTxV3(tx) { + if common.IsValidRelayedTxV3(tx) { relayedTx := tx.(data.RelayedTransactionHandler) payerAddress = relayedTx.GetRelayerAddr() relayerAccount, err := txv.accounts.GetExistingAccount(payerAddress) diff --git a/process/economics/economicsData.go b/process/economics/economicsData.go index dfce9d5a6f6..1e4ebe57454 100644 --- a/process/economics/economicsData.go +++ b/process/economics/economicsData.go @@ -611,7 +611,7 @@ func (ed *economicsData) ComputeGasLimitBasedOnBalanceInEpoch(tx data.Transactio // getExtraGasLimitRelayedTx returns extra gas limit for relayed tx in a specific epoch func (ed *economicsData) getExtraGasLimitRelayedTx(txInstance *transaction.Transaction, epoch uint32) uint64 { - if common.IsRelayedTxV3(txInstance) { + if common.IsValidRelayedTxV3(txInstance) { return ed.MinGasLimitInEpoch(epoch) } diff --git a/process/transaction/baseProcess.go b/process/transaction/baseProcess.go index d480ec22b51..fec873a0fa6 100644 --- a/process/transaction/baseProcess.go +++ b/process/transaction/baseProcess.go @@ -171,7 +171,10 @@ func (txProc *baseTxProcessor) checkTxValues( txFee = core.SafeMul(tx.GasLimit, tx.GasPrice) } - // early exit for relayed v3, the cost will be compared with the sender balance + // early exit for relayed v3. This check is done on the relayed transaction, thus + // the fee payer at this point should be the relayer. + // The check for the cost (fee + value), will be done later on, as part of checkUserTxOfRelayedV3Values + // on the sender account, after relayed moved the fee. if isRelayedV3 { return nil } @@ -184,11 +187,65 @@ func (txProc *baseTxProcessor) checkTxValues( return nil } +func (txProc *baseTxProcessor) checkUserTxOfRelayedV3Values( + tx *transaction.Transaction, + acntSnd, acntDst state.UserAccountHandler, +) error { + err := txProc.verifyGuardian(tx, acntSnd) + if err != nil { + return err + } + err = txProc.checkUserNames(tx, acntSnd, acntDst) + if err != nil { + return err + } + if check.IfNil(acntSnd) { + return nil + } + if acntSnd.GetNonce() < tx.Nonce { + return process.ErrHigherNonceInTransaction + } + if acntSnd.GetNonce() > tx.Nonce { + return process.ErrLowerNonceInTransaction + } + err = txProc.economicsFee.CheckValidityTxValues(tx) + if err != nil { + return err + } + + if tx.GasLimit < txProc.economicsFee.ComputeGasLimit(tx) { + return process.ErrNotEnoughGasInUserTx + } + + txFee := txProc.computeInnerTxFee(tx) + + if acntSnd.GetBalance().Cmp(txFee) < 0 { + return fmt.Errorf("%w, has: %s, wanted: %s", + process.ErrInsufficientFee, + acntSnd.GetBalance().String(), + txFee.String(), + ) + } + + if !txProc.enableEpochsHandler.IsFlagEnabled(common.PenalizedTooMuchGasFlag) { + // backwards compatibility issue when provided gas limit and gas price exceeds the available balance before the + // activation of the "penalize too much gas" flag + txFee = core.SafeMul(tx.GasLimit, tx.GasPrice) + } + + cost := big.NewInt(0).Add(txFee, tx.Value) + if acntSnd.GetBalance().Cmp(cost) < 0 { + return process.ErrInsufficientFunds + } + + return nil +} + func (txProc *baseTxProcessor) getFeePayer( tx *transaction.Transaction, acntSnd state.UserAccountHandler, ) (state.UserAccountHandler, bool, error) { - if !common.IsRelayedTxV3(tx) { + if !common.IsValidRelayedTxV3(tx) { return acntSnd, false, nil } diff --git a/process/transaction/interceptedTransaction.go b/process/transaction/interceptedTransaction.go index 9afe05c02f3..9efd86c321a 100644 --- a/process/transaction/interceptedTransaction.go +++ b/process/transaction/interceptedTransaction.go @@ -212,7 +212,7 @@ func (inTx *InterceptedTransaction) CheckValidity() error { } func (inTx *InterceptedTransaction) checkRecursiveRelayed(userTx *transaction.Transaction) error { - if common.IsRelayedTxV3(userTx) { + if common.IsValidRelayedTxV3(userTx) { return process.ErrRecursiveRelayedTxIsNotAllowed } @@ -234,7 +234,7 @@ func isRelayedTx(funcName string) bool { } func (inTx *InterceptedTransaction) verifyIfRelayedTxV3(tx *transaction.Transaction) error { - if !common.IsRelayedTxV3(tx) { + if !common.IsValidRelayedTxV3(tx) { return nil } diff --git a/process/transaction/shardProcess.go b/process/transaction/shardProcess.go index 6637a8466d2..11d82f1acfc 100644 --- a/process/transaction/shardProcess.go +++ b/process/transaction/shardProcess.go @@ -225,7 +225,7 @@ func (txProc *txProcessor) ProcessTransaction(tx *transaction.Transaction) (vmco switch txType { case process.MoveBalance: - err = txProc.processMoveBalance(tx, acntSnd, acntDst, dstShardTxType, nil, false) + err = txProc.processMoveBalance(tx, acntSnd, acntDst, dstShardTxType, nil, false, false) if err != nil { return vmcommon.UserError, txProc.executeAfterFailedMoveBalanceTransaction(tx, err) } @@ -290,15 +290,15 @@ func (txProc *txProcessor) executeAfterFailedMoveBalanceTransaction( func (txProc *txProcessor) executingFailedTransaction( tx *transaction.Transaction, - acntSnd state.UserAccountHandler, + relayerAccount state.UserAccountHandler, txError error, ) error { - if check.IfNil(acntSnd) { + if check.IfNil(relayerAccount) { return nil } txFee := txProc.economicsFee.ComputeTxFee(tx) - err := acntSnd.SubFromBalance(txFee) + err := relayerAccount.SubFromBalance(txFee) if err != nil { return err } @@ -308,7 +308,7 @@ func (txProc *txProcessor) executingFailedTransaction( return err } - acntSnd.IncreaseNonce(1) + relayerAccount.IncreaseNonce(1) err = txProc.badTxForwarder.AddIntermediateTransactions([]data.TransactionHandler{tx}, txHash) if err != nil { return err @@ -318,7 +318,7 @@ func (txProc *txProcessor) executingFailedTransaction( rpt := &receipt.Receipt{ Value: big.NewInt(0).Set(txFee), - SndAddr: acntSnd.AddressBytes(), + SndAddr: relayerAccount.AddressBytes(), Data: []byte(txError.Error()), TxHash: txHash, } @@ -330,7 +330,7 @@ func (txProc *txProcessor) executingFailedTransaction( txProc.txFeeHandler.ProcessTransactionFee(txFee, big.NewInt(0), txHash) - err = txProc.accounts.SaveAccount(acntSnd) + err = txProc.accounts.SaveAccount(relayerAccount) if err != nil { return err } @@ -464,6 +464,7 @@ func (txProc *txProcessor) processMoveBalance( destShardTxType process.TransactionType, originalTxHash []byte, isUserTxOfRelayed bool, + isUserTxOfRelayedV3 bool, ) error { moveBalanceCost, totalCost, err := txProc.processTxFee(tx, acntSrc, acntDst, destShardTxType, isUserTxOfRelayed) @@ -525,9 +526,12 @@ func (txProc *txProcessor) processMoveBalance( } } - txHash, err := core.CalculateHash(txProc.marshalizer, txProc.hasher, tx) - if err != nil { - return err + txHash := originalTxHash + if !isUserTxOfRelayedV3 { + txHash, err = core.CalculateHash(txProc.marshalizer, txProc.hasher, tx) + if err != nil { + return err + } } err = txProc.createReceiptWithReturnedGas(txHash, tx, acntSrc, moveBalanceCost, totalCost, destShardTxType, isUserTxOfRelayed) @@ -600,25 +604,18 @@ func makeUserTxFromRelayedTxV2Args(args [][]byte) *transaction.Transaction { return userTx } -func (txProc *txProcessor) finishExecutionOfRelayedTx( +func (txProc *txProcessor) finishExecutionOfRelayedTxV3( relayerAcnt, acntDst state.UserAccountHandler, tx *transaction.Transaction, userTx *transaction.Transaction, ) (vmcommon.ReturnCode, error) { - isUserTxOfRelayedV3 := common.IsRelayedTxV3(tx) - relayedValue := tx.Value - if isUserTxOfRelayedV3 { - relayedValue = big.NewInt(0) - } - - computedFees := txProc.computeRelayedTxFees(tx, userTx, isUserTxOfRelayedV3) - txHash, err := txProc.processTxAtRelayer( + computedFees := txProc.computeRelayedTxV3Fees(tx, userTx) + txHash, err := txProc.processTxV3AtRelayer( relayerAcnt, computedFees.totalFee, computedFees.relayerFee, tx, - relayedValue, - isUserTxOfRelayedV3) + big.NewInt(0)) if err != nil { return 0, err } @@ -627,18 +624,43 @@ func (txProc *txProcessor) finishExecutionOfRelayedTx( return vmcommon.Ok, nil } - err = txProc.addFeeAndValueToDest(acntDst, relayedValue, computedFees.remainingFee) + err = txProc.addFeeAndValueToDest(acntDst, big.NewInt(0), computedFees.remainingFee) if err != nil { return 0, err } relayedNonce := tx.Nonce - relayerAddr := tx.SndAddr - if isUserTxOfRelayedV3 && !check.IfNil(relayerAcnt) { - relayerAddr = tx.RelayerAddr + relayerAddr := tx.RelayerAddr + + return txProc.processUserTxOfRelayedV3(tx, userTx, relayedNonce, relayerAddr, txHash) +} + +func (txProc *txProcessor) finishExecutionOfRelayedTx( + relayerAcnt, acntDst state.UserAccountHandler, + tx *transaction.Transaction, + userTx *transaction.Transaction, +) (vmcommon.ReturnCode, error) { + computedFees := txProc.computeRelayedTxFees(tx, userTx) + txHash, err := txProc.processTxAtRelayer( + relayerAcnt, + computedFees.totalFee, + computedFees.relayerFee, + tx, + tx.Value) + if err != nil { + return 0, err + } + + if check.IfNil(acntDst) { + return vmcommon.Ok, nil + } + + err = txProc.addFeeAndValueToDest(acntDst, tx.Value, computedFees.remainingFee) + if err != nil { + return 0, err } - return txProc.processUserTx(tx, userTx, relayedValue, relayedNonce, relayerAddr, txHash) + return txProc.processUserTx(tx, userTx, tx.Value, tx.Nonce, tx.SndAddr, txHash) } func (txProc *txProcessor) processTxAtRelayer( @@ -647,7 +669,6 @@ func (txProc *txProcessor) processTxAtRelayer( relayerFee *big.Int, tx *transaction.Transaction, valueToSubFromRelayer *big.Int, - isUserTxOfRelayedV3 bool, ) ([]byte, error) { txHash, err := core.CalculateHash(txProc.marshalizer, txProc.hasher, tx) if err != nil { @@ -665,9 +686,41 @@ func (txProc *txProcessor) processTxAtRelayer( return nil, err } - if !isUserTxOfRelayedV3 { // won't increase relayer nonce for v3 - relayerAcnt.IncreaseNonce(1) + relayerAcnt.IncreaseNonce(1) + err = txProc.accounts.SaveAccount(relayerAcnt) + if err != nil { + return nil, err + } + + txProc.txFeeHandler.ProcessTransactionFee(relayerFee, big.NewInt(0), txHash) + } + + return txHash, nil +} + +func (txProc *txProcessor) processTxV3AtRelayer( + relayerAcnt state.UserAccountHandler, + totalFee *big.Int, + relayerFee *big.Int, + tx *transaction.Transaction, + valueToSubFromRelayer *big.Int, +) ([]byte, error) { + txHash, err := core.CalculateHash(txProc.marshalizer, txProc.hasher, tx) + if err != nil { + return nil, err + } + + if !check.IfNil(relayerAcnt) { + err = relayerAcnt.SubFromBalance(valueToSubFromRelayer) + if err != nil { + return nil, err + } + + err = relayerAcnt.SubFromBalance(totalFee) + if err != nil { + return nil, err } + err = txProc.accounts.SaveAccount(relayerAcnt) if err != nil { return nil, err @@ -712,8 +765,8 @@ func (txProc *txProcessor) processRelayedTxV3(tx *transaction.Transaction) (vmco } userTx := *tx - // remove relayer addr and signature for tx type handler - userTx.RelayerAddr = nil + // remove relayer signature for tx type handler + // hash of this user tx won't be computed/used, but the originalTxHash userTx.RelayerSignature = nil minGasLimit := txProc.economicsFee.MinGasLimit() userTx.GasLimit = userTx.GasLimit - minGasLimit @@ -721,7 +774,7 @@ func (txProc *txProcessor) processRelayedTxV3(tx *transaction.Transaction) (vmco return vmcommon.UserError, txProc.executingFailedTransaction(tx, relayerAccount, process.ErrInsufficientGasLimitInTx) } - return txProc.finishExecutionOfRelayedTx(relayerAccount, sndAccount, tx, &userTx) + return txProc.finishExecutionOfRelayedTxV3(relayerAccount, sndAccount, tx, &userTx) } func (txProc *txProcessor) processRelayedTxV2( @@ -791,17 +844,12 @@ func (txProc *txProcessor) processRelayedTx( return txProc.finishExecutionOfRelayedTx(relayerAcnt, acntDst, tx, userTx) } -func (txProc *txProcessor) computeRelayedTxFees(tx, userTx *transaction.Transaction, isUserTxOfRelayedV3 bool) relayedFees { +func (txProc *txProcessor) computeRelayedTxFees(tx, userTx *transaction.Transaction) relayedFees { relayerFee := txProc.economicsFee.ComputeMoveBalanceFee(tx) totalFee := txProc.economicsFee.ComputeTxFee(tx) if txProc.enableEpochsHandler.IsFlagEnabled(common.FixRelayedBaseCostFlag) { userFee := txProc.computeInnerTxFeeAfterBaseCostFix(userTx) - if isUserTxOfRelayedV3 { - relayerGas := txProc.economicsFee.MinGasLimit() - relayerFee = core.SafeMul(relayerGas, tx.GasPrice) - } - totalFee = totalFee.Add(relayerFee, userFee) } remainingFee := big.NewInt(0).Sub(totalFee, relayerFee) @@ -815,6 +863,23 @@ func (txProc *txProcessor) computeRelayedTxFees(tx, userTx *transaction.Transact return computedFees } +func (txProc *txProcessor) computeRelayedTxV3Fees(tx, userTx *transaction.Transaction) relayedFees { + relayerGas := txProc.economicsFee.MinGasLimit() + relayerFee := core.SafeMul(relayerGas, tx.GasPrice) + + userFee := txProc.computeInnerTxFeeAfterBaseCostFix(userTx) + + totalFee := big.NewInt(0).Add(relayerFee, userFee) + + computedFees := relayedFees{ + totalFee: totalFee, + remainingFee: userFee, + relayerFee: relayerFee, + } + + return computedFees +} + func (txProc *txProcessor) removeValueAndConsumedFeeFromUser( userTx *transaction.Transaction, relayedTxValue *big.Int, @@ -943,7 +1008,7 @@ func (txProc *txProcessor) processUserTx( returnCode := vmcommon.Ok switch txType { case process.MoveBalance: - err = txProc.processMoveBalance(userTx, acntSnd, acntDst, dstShardTxType, originalTxHash, true) + err = txProc.processMoveBalance(userTx, acntSnd, acntDst, dstShardTxType, originalTxHash, true, false) case process.SCDeployment: err = txProc.processMoveBalanceCostRelayedUserTx(userTx, scrFromTx, acntSnd, originalTxHash) if err != nil { @@ -1018,6 +1083,130 @@ func (txProc *txProcessor) processUserTx( return vmcommon.Ok, nil } +func (txProc *txProcessor) processUserTxOfRelayedV3( + originalTx *transaction.Transaction, + userTx *transaction.Transaction, + relayedNonce uint64, + relayerAddr []byte, + originalTxHash []byte, +) (vmcommon.ReturnCode, error) { + + acntSnd, acntDst, err := txProc.getAccounts(userTx.SndAddr, userTx.RcvAddr) + if err != nil { + errRemove := txProc.removeValueAndConsumedFeeFromUser(userTx, big.NewInt(0), originalTxHash, originalTx, err) + if errRemove != nil { + return vmcommon.UserError, errRemove + } + return vmcommon.UserError, txProc.executeFailedRelayedUserTx( + userTx, + relayerAddr, + big.NewInt(0), + relayedNonce, + originalTx, + originalTxHash, + err.Error()) + } + + txType, dstShardTxType := txProc.txTypeHandler.ComputeTransactionType(userTx) + err = txProc.checkUserTxOfRelayedV3Values(userTx, acntSnd, acntDst) + if err != nil { + errRemove := txProc.removeValueAndConsumedFeeFromUser(userTx, big.NewInt(0), originalTxHash, originalTx, err) + if errRemove != nil { + return vmcommon.UserError, errRemove + } + return vmcommon.UserError, txProc.executeFailedRelayedUserTx( + userTx, + relayerAddr, + big.NewInt(0), + relayedNonce, + originalTx, + originalTxHash, + err.Error()) + } + + scrFromTx, err := txProc.makeSCRFromUserTx(userTx, relayerAddr, big.NewInt(0), originalTxHash) + if err != nil { + return 0, err + } + + returnCode := vmcommon.Ok + switch txType { + case process.MoveBalance: + err = txProc.processMoveBalance(userTx, acntSnd, acntDst, dstShardTxType, originalTxHash, true, true) + case process.SCDeployment: + err = txProc.processMoveBalanceCostRelayedUserTx(userTx, scrFromTx, acntSnd, originalTxHash) + if err != nil { + break + } + + returnCode, err = txProc.scProcessor.DeploySmartContract(scrFromTx, acntSnd) + case process.SCInvoking: + err = txProc.processMoveBalanceCostRelayedUserTx(userTx, scrFromTx, acntSnd, originalTxHash) + if err != nil { + break + } + + returnCode, err = txProc.scProcessor.ExecuteSmartContractTransaction(scrFromTx, acntSnd, acntDst) + case process.BuiltInFunctionCall: + err = txProc.processMoveBalanceCostRelayedUserTx(userTx, scrFromTx, acntSnd, originalTxHash) + if err != nil { + break + } + + returnCode, err = txProc.scProcessor.ExecuteBuiltInFunction(scrFromTx, acntSnd, acntDst) + default: + err = process.ErrWrongTransaction + errRemove := txProc.removeValueAndConsumedFeeFromUser(userTx, big.NewInt(0), originalTxHash, originalTx, err) + if errRemove != nil { + return vmcommon.UserError, errRemove + } + return vmcommon.UserError, txProc.executeFailedRelayedUserTx( + userTx, + relayerAddr, + big.NewInt(0), + relayedNonce, + originalTx, + originalTxHash, + err.Error()) + } + + if errors.Is(err, process.ErrInvalidMetaTransaction) || errors.Is(err, process.ErrAccountNotPayable) { + return vmcommon.UserError, txProc.executeFailedRelayedUserTx( + userTx, + relayerAddr, + big.NewInt(0), + relayedNonce, + originalTx, + originalTxHash, + err.Error()) + } + + if errors.Is(err, process.ErrFailedTransaction) { + // in case of failed inner user tx transaction we should just simply return execution failed and + // not failed transaction - as the actual transaction (the relayed we correctly executed) and thus + // it should not lend in the invalid miniblock + return vmcommon.ExecutionFailed, nil + } + + if err != nil { + log.Error("processUserTx", "protocolError", err) + return vmcommon.ExecutionFailed, err + } + + // no need to add the smart contract result From TX to the intermediate transactions in case of error + // returning value is resolved inside smart contract processor or above by executeFailedRelayedUserTx + if returnCode != vmcommon.Ok { + return returnCode, nil + } + + err = txProc.scrForwarder.AddIntermediateTransactions([]data.TransactionHandler{scrFromTx}, originalTxHash) + if err != nil { + return 0, err + } + + return vmcommon.Ok, nil +} + func (txProc *baseTxProcessor) isCrossTxFromMe(adrSrc, adrDst []byte) bool { shardForSrc := txProc.shardCoordinator.ComputeId(adrSrc) shardForDst := txProc.shardCoordinator.ComputeId(adrDst) From ea799ab74103af94b86408aecb4e938cb05bb6ee Mon Sep 17 00:00:00 2001 From: Sorin Stanculeanu Date: Tue, 19 Nov 2024 11:51:59 +0200 Subject: [PATCH 14/39] updated indexer --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 4ea01439b9c..8a16cd548bd 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( github.com/multiversx/mx-chain-communication-go v1.1.1 github.com/multiversx/mx-chain-core-go v1.2.24-0.20241029140551-8ed69b598c83 github.com/multiversx/mx-chain-crypto-go v1.2.12 - github.com/multiversx/mx-chain-es-indexer-go v1.7.10 + github.com/multiversx/mx-chain-es-indexer-go v1.7.11-0.20241118100151-956a1f23c5c1 github.com/multiversx/mx-chain-logger-go v1.0.15 github.com/multiversx/mx-chain-scenario-go v1.4.4 github.com/multiversx/mx-chain-storage-go v1.0.16 diff --git a/go.sum b/go.sum index 2982014a282..7c4551cae64 100644 --- a/go.sum +++ b/go.sum @@ -391,8 +391,8 @@ github.com/multiversx/mx-chain-core-go v1.2.24-0.20241029140551-8ed69b598c83 h1: github.com/multiversx/mx-chain-core-go v1.2.24-0.20241029140551-8ed69b598c83/go.mod h1:B5zU4MFyJezmEzCsAHE9YNULmGCm2zbPHvl9hazNxmE= github.com/multiversx/mx-chain-crypto-go v1.2.12 h1:zWip7rpUS4CGthJxfKn5MZfMfYPjVjIiCID6uX5BSOk= github.com/multiversx/mx-chain-crypto-go v1.2.12/go.mod h1:HzcPpCm1zanNct/6h2rIh+MFrlXbjA5C8+uMyXj3LI4= -github.com/multiversx/mx-chain-es-indexer-go v1.7.10 h1:Umi7WN8h4BOXLw7CM3VgvaWkLGef7nXtaPIGbjBCT3U= -github.com/multiversx/mx-chain-es-indexer-go v1.7.10/go.mod h1:oGcRK2E3Syv6vRTszWrrb/TqD8akq0yeoMr1wPPiTO4= +github.com/multiversx/mx-chain-es-indexer-go v1.7.11-0.20241118100151-956a1f23c5c1 h1:wgMxgtUWd9//FPCTOLj/75j9Kwnd9PE2tHk0KLIFF6s= +github.com/multiversx/mx-chain-es-indexer-go v1.7.11-0.20241118100151-956a1f23c5c1/go.mod h1:/KoFDVgh9kGYiINm2THJsII7jfxmbTXWtBoSS1dJo1w= github.com/multiversx/mx-chain-logger-go v1.0.15 h1:HlNdK8etyJyL9NQ+6mIXyKPEBo+wRqOwi3n+m2QIHXc= github.com/multiversx/mx-chain-logger-go v1.0.15/go.mod h1:t3PRKaWB1M+i6gUfD27KXgzLJJC+mAQiN+FLlL1yoGQ= github.com/multiversx/mx-chain-scenario-go v1.4.4 h1:DVE2V+FPeyD/yWoC+KEfPK3jsFzHeruelESfpTlf460= From dab1093b05ef7509121ea34989ad28df51406cf2 Mon Sep 17 00:00:00 2001 From: Sorin Stanculeanu Date: Fri, 22 Nov 2024 16:47:52 +0200 Subject: [PATCH 15/39] proper name for EnableEpoch epoch --- cmd/node/config/enableEpochs.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/node/config/enableEpochs.toml b/cmd/node/config/enableEpochs.toml index 9f0261959ea..cffdd5ea773 100644 --- a/cmd/node/config/enableEpochs.toml +++ b/cmd/node/config/enableEpochs.toml @@ -333,8 +333,8 @@ # FixRelayedMoveBalanceToNonPayableSCEnableEpoch represents the epoch when the fix for relayed move balance to non payable sc will be enabled FixRelayedMoveBalanceToNonPayableSCEnableEpoch = 1 - # RelayedTransactionsV3 represents the epoch when the relayed transactions v3 will be enabled - RelayedTransactionsV3 = 5 + # RelayedTransactionsV3EnableEpoch represents the epoch when the relayed transactions v3 will be enabled + RelayedTransactionsV3EnableEpoch = 2 # BLSMultiSignerEnableEpoch represents the activation epoch for different types of BLS multi-signers BLSMultiSignerEnableEpoch = [ From 95fac83754d0281823b12c41d503e18d64511156 Mon Sep 17 00:00:00 2001 From: Sorin Stanculeanu Date: Mon, 25 Nov 2024 12:47:49 +0200 Subject: [PATCH 16/39] fixes after review --- process/transaction/shardProcess.go | 73 ++++++++++++++++++++++++++--- 1 file changed, 66 insertions(+), 7 deletions(-) diff --git a/process/transaction/shardProcess.go b/process/transaction/shardProcess.go index 11d82f1acfc..8dfd7f884d3 100644 --- a/process/transaction/shardProcess.go +++ b/process/transaction/shardProcess.go @@ -290,12 +290,66 @@ func (txProc *txProcessor) executeAfterFailedMoveBalanceTransaction( func (txProc *txProcessor) executingFailedTransaction( tx *transaction.Transaction, + acntSnd state.UserAccountHandler, + txError error, +) error { + if check.IfNil(acntSnd) { + return nil + } + + txFee := txProc.economicsFee.ComputeTxFee(tx) + err := acntSnd.SubFromBalance(txFee) + if err != nil { + return err + } + + txHash, err := core.CalculateHash(txProc.marshalizer, txProc.hasher, tx) + if err != nil { + return err + } + + acntSnd.IncreaseNonce(1) + err = txProc.badTxForwarder.AddIntermediateTransactions([]data.TransactionHandler{tx}, txHash) + if err != nil { + return err + } + + log.Trace("executingFailedTransaction", "fail reason(error)", txError, "tx hash", txHash) + + rpt := &receipt.Receipt{ + Value: big.NewInt(0).Set(txFee), + SndAddr: tx.SndAddr, + Data: []byte(txError.Error()), + TxHash: txHash, + } + + err = txProc.receiptForwarder.AddIntermediateTransactions([]data.TransactionHandler{rpt}, txHash) + if err != nil { + return err + } + + txProc.txFeeHandler.ProcessTransactionFee(txFee, big.NewInt(0), txHash) + + err = txProc.accounts.SaveAccount(acntSnd) + if err != nil { + return err + } + + return process.ErrFailedTransaction +} + +func (txProc *txProcessor) executingFailedTransactionRelayedV3( + tx *transaction.Transaction, + acntSnd state.UserAccountHandler, relayerAccount state.UserAccountHandler, txError error, ) error { if check.IfNil(relayerAccount) { return nil } + if check.IfNil(acntSnd) { + return nil + } txFee := txProc.economicsFee.ComputeTxFee(tx) err := relayerAccount.SubFromBalance(txFee) @@ -308,13 +362,13 @@ func (txProc *txProcessor) executingFailedTransaction( return err } - relayerAccount.IncreaseNonce(1) + acntSnd.IncreaseNonce(1) err = txProc.badTxForwarder.AddIntermediateTransactions([]data.TransactionHandler{tx}, txHash) if err != nil { return err } - log.Trace("executingFailedTransaction", "fail reason(error)", txError, "tx hash", txHash) + log.Trace("executingFailedTransactionRelayedV3", "fail reason(error)", txError, "tx hash", txHash) rpt := &receipt.Receipt{ Value: big.NewInt(0).Set(txFee), @@ -335,6 +389,11 @@ func (txProc *txProcessor) executingFailedTransaction( return err } + err = txProc.accounts.SaveAccount(acntSnd) + if err != nil { + return err + } + return process.ErrFailedTransaction } @@ -610,7 +669,7 @@ func (txProc *txProcessor) finishExecutionOfRelayedTxV3( userTx *transaction.Transaction, ) (vmcommon.ReturnCode, error) { computedFees := txProc.computeRelayedTxV3Fees(tx, userTx) - txHash, err := txProc.processTxV3AtRelayer( + txHash, err := txProc.consumeFeeFromRelayer( relayerAcnt, computedFees.totalFee, computedFees.relayerFee, @@ -698,7 +757,7 @@ func (txProc *txProcessor) processTxAtRelayer( return txHash, nil } -func (txProc *txProcessor) processTxV3AtRelayer( +func (txProc *txProcessor) consumeFeeFromRelayer( relayerAcnt state.UserAccountHandler, totalFee *big.Int, relayerFee *big.Int, @@ -753,15 +812,15 @@ func (txProc *txProcessor) processRelayedTxV3(tx *transaction.Transaction) (vmco } if !txProc.enableEpochsHandler.IsFlagEnabled(common.RelayedTransactionsV3Flag) { - return vmcommon.UserError, txProc.executingFailedTransaction(tx, relayerAccount, process.ErrRelayedTxV3Disabled) + return vmcommon.UserError, txProc.executingFailedTransactionRelayedV3(tx, sndAccount, relayerAccount, process.ErrRelayedTxV3Disabled) } if !txProc.shardCoordinator.SameShard(tx.RelayerAddr, tx.SndAddr) { - return vmcommon.UserError, txProc.executingFailedTransaction(tx, relayerAccount, process.ErrShardIdMissmatch) + return vmcommon.UserError, txProc.executingFailedTransactionRelayedV3(tx, sndAccount, relayerAccount, process.ErrShardIdMissmatch) } if !check.IfNil(relayerAccount) && relayerAccount.IsGuarded() { - return vmcommon.UserError, txProc.executingFailedTransaction(tx, relayerAccount, process.ErrGuardedRelayerNotAllowed) + return vmcommon.UserError, txProc.executingFailedTransactionRelayedV3(tx, sndAccount, relayerAccount, process.ErrGuardedRelayerNotAllowed) } userTx := *tx From 0c2fe332c20e1675b94cf32a590f85e5163bd3ba Mon Sep 17 00:00:00 2001 From: Sorin Stanculeanu Date: Tue, 26 Nov 2024 18:18:09 +0200 Subject: [PATCH 17/39] fixes after tests: fix validation error + fix txs to meta --- .../relayedTx/relayedTx_test.go | 68 +++++++++++++++++++ node/node.go | 10 ++- node/node_test.go | 34 +++++++--- process/transaction/metaProcess.go | 2 + 4 files changed, 104 insertions(+), 10 deletions(-) diff --git a/integrationTests/chainSimulator/relayedTx/relayedTx_test.go b/integrationTests/chainSimulator/relayedTx/relayedTx_test.go index eabad3d708a..63f1828d451 100644 --- a/integrationTests/chainSimulator/relayedTx/relayedTx_test.go +++ b/integrationTests/chainSimulator/relayedTx/relayedTx_test.go @@ -20,6 +20,7 @@ import ( "github.com/multiversx/mx-chain-go/node/chainSimulator/dtos" chainSimulatorProcess "github.com/multiversx/mx-chain-go/node/chainSimulator/process" "github.com/multiversx/mx-chain-go/process" + "github.com/multiversx/mx-chain-go/vm" "github.com/stretchr/testify/require" ) @@ -60,6 +61,8 @@ func TestRelayedV3WithChainSimulator(t *testing.T) { t.Run("cross shard sc call, invalid gas", testRelayedV3ScCallInvalidGasLimit(0, 1)) t.Run("intra shard sc call, invalid method", testRelayedV3ScCallInvalidMethod(0, 0)) t.Run("cross shard sc call, invalid method", testRelayedV3ScCallInvalidMethod(0, 1)) + + t.Run("create new delegation contract", testRelayedV3MetaInteraction()) } func testRelayedV3MoveBalance( @@ -513,6 +516,71 @@ func testRelayedV3ScCallInvalidMethod( } } +func testRelayedV3MetaInteraction() func(t *testing.T) { + return func(t *testing.T) { + if testing.Short() { + t.Skip("this is not a short test") + } + + providedActivationEpoch := uint32(1) + alterConfigsFunc := func(cfg *config.Configs) { + cfg.EpochConfig.EnableEpochs.FixRelayedBaseCostEnableEpoch = providedActivationEpoch + cfg.EpochConfig.EnableEpochs.RelayedTransactionsV3EnableEpoch = providedActivationEpoch + } + + cs := startChainSimulator(t, alterConfigsFunc) + defer cs.Close() + + relayerShard := uint32(0) + + initialBalance := big.NewInt(0).Mul(oneEGLD, big.NewInt(10000)) + relayer, err := cs.GenerateAndMintWalletAddress(relayerShard, initialBalance) + require.NoError(t, err) + + sender, err := cs.GenerateAndMintWalletAddress(relayerShard, initialBalance) + require.NoError(t, err) + + // generate one block so the minting has effect + err = cs.GenerateBlocks(1) + require.NoError(t, err) + + // send relayed tx with invalid value + txDataAdd := "createNewDelegationContract@00@00" + gasLimit := uint64(60000000) + value := big.NewInt(0).Mul(oneEGLD, big.NewInt(1250)) + relayedTx := generateRelayedV3Transaction(sender.Bytes, 0, vm.DelegationManagerSCAddress, relayer.Bytes, value, txDataAdd, gasLimit) + + relayerBefore := getBalance(t, cs, relayer) + senderBefore := getBalance(t, cs, sender) + + result, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(relayedTx, maxNumOfBlocksToGenerateWhenExecutingTx) + require.NoError(t, err) + + require.NoError(t, cs.GenerateBlocks(maxNumOfBlocksToGenerateWhenExecutingTx)) + + relayerAfter := getBalance(t, cs, relayer) + senderAfter := getBalance(t, cs, sender) + + // check consumed fees + refund := getRefundValue(result.SmartContractResults) + consumedFee := big.NewInt(0).Sub(relayerBefore, relayerAfter) + + gasForFullPrice := uint64(len(txDataAdd)*gasPerDataByte + minGasLimit + minGasLimit) + gasForDeductedPrice := gasLimit - gasForFullPrice + deductedGasPrice := uint64(minGasPrice / deductionFactor) + initialFee := gasForFullPrice*minGasPrice + gasForDeductedPrice*deductedGasPrice + initialFeeInt := big.NewInt(0).SetUint64(initialFee) + expectedConsumedFee := big.NewInt(0).Sub(initialFeeInt, refund) + + gasUsed := gasForFullPrice + gasForDeductedPrice - refund.Uint64()/deductedGasPrice + require.Equal(t, expectedConsumedFee.String(), consumedFee.String()) + require.Equal(t, value.String(), big.NewInt(0).Sub(senderBefore, senderAfter).String(), "sender should have consumed the value only") + require.Equal(t, initialFeeInt.String(), result.InitiallyPaidFee) + require.Equal(t, expectedConsumedFee.String(), result.Fee) + require.Equal(t, gasUsed, result.GasUsed) + } +} + func TestFixRelayedMoveBalanceWithChainSimulator(t *testing.T) { if testing.Short() { t.Skip("this is not a short test") diff --git a/node/node.go b/node/node.go index ca07f4a0acf..6dec8372c04 100644 --- a/node/node.go +++ b/node/node.go @@ -723,7 +723,7 @@ func (n *Node) ValidateTransaction(tx *transaction.Transaction) error { if errors.Is(err, process.ErrAccountNotFound) { return fmt.Errorf("%w for address %s", process.ErrInsufficientFunds, - n.coreComponents.AddressPubKeyConverter().SilentEncode(tx.SndAddr, log), + n.getFeePayer(tx), ) } @@ -1545,6 +1545,14 @@ func (n *Node) getKeyBytes(key string) ([]byte, error) { return hex.DecodeString(key) } +func (n *Node) getFeePayer(tx *transaction.Transaction) string { + if common.IsValidRelayedTxV3(tx) { + return n.coreComponents.AddressPubKeyConverter().SilentEncode(tx.GetRelayerAddr(), log) + } + + return n.coreComponents.AddressPubKeyConverter().SilentEncode(tx.GetSndAddr(), log) +} + // IsInterfaceNil returns true if there is no value under the interface func (n *Node) IsInterfaceNil() bool { return n == nil diff --git a/node/node_test.go b/node/node_test.go index 319890a5d0d..cd064e6087b 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -3004,15 +3004,31 @@ func TestValidateTransaction_ShouldAdaptAccountNotFoundError(t *testing.T) { node.WithCryptoComponents(getDefaultCryptoComponents()), ) - tx := &transaction.Transaction{ - SndAddr: bytes.Repeat([]byte("1"), 32), - RcvAddr: bytes.Repeat([]byte("1"), 32), - Value: big.NewInt(37), - Signature: []byte("signature"), - ChainID: []byte("chainID"), - } - err := n.ValidateTransaction(tx) - require.Equal(t, "insufficient funds for address erd1xycnzvf3xycnzvf3xycnzvf3xycnzvf3xycnzvf3xycnzvf3xycspcqad6", err.Error()) + t.Run("normal tx", func(t *testing.T) { + tx := &transaction.Transaction{ + SndAddr: bytes.Repeat([]byte("1"), 32), + RcvAddr: bytes.Repeat([]byte("1"), 32), + Value: big.NewInt(37), + Signature: []byte("signature"), + ChainID: []byte("chainID"), + } + + err := n.ValidateTransaction(tx) + require.Equal(t, "insufficient funds for address erd1xycnzvf3xycnzvf3xycnzvf3xycnzvf3xycnzvf3xycnzvf3xycspcqad6", err.Error()) + }) + t.Run("relayed tx v3", func(t *testing.T) { + tx := &transaction.Transaction{ + SndAddr: bytes.Repeat([]byte("1"), 32), + RcvAddr: bytes.Repeat([]byte("1"), 32), + Value: big.NewInt(37), + Signature: []byte("sSignature"), + RelayerAddr: bytes.Repeat([]byte("2"), 32), + RelayerSignature: []byte("rSignature"), + ChainID: []byte("chainID"), + } + err := n.ValidateTransaction(tx) + require.Equal(t, "insufficient funds for address erd1xgeryv3jxgeryv3jxgeryv3jxgeryv3jxgeryv3jxgeryv3jxgeqvw86cj", err.Error()) + }) } func TestCreateShardedStores_NilShardCoordinatorShouldError(t *testing.T) { diff --git a/process/transaction/metaProcess.go b/process/transaction/metaProcess.go index 13d6fd4715b..bf3bceb2780 100644 --- a/process/transaction/metaProcess.go +++ b/process/transaction/metaProcess.go @@ -145,6 +145,8 @@ func (txProc *metaTxProcessor) ProcessTransaction(tx *transaction.Transaction) ( if txProc.enableEpochsHandler.IsFlagEnabled(common.ESDTFlag) { return txProc.processSCInvoking(tx, tx.SndAddr, tx.RcvAddr) } + case process.RelayedTxV3: + return vmcommon.Ok, nil // it will be processed through the scr created on source } snapshot := txProc.accounts.JournalLen() From 2c05aa7b85713be1e07c1dc23c0713a8a868509d Mon Sep 17 00:00:00 2001 From: Sorin Stanculeanu Date: Tue, 26 Nov 2024 18:53:13 +0200 Subject: [PATCH 18/39] proper fix for wrapped error --- node/node.go | 18 +++++++++++++----- node/node_test.go | 34 +++++++++++++++++++++++++++++++++- 2 files changed, 46 insertions(+), 6 deletions(-) diff --git a/node/node.go b/node/node.go index 6dec8372c04..512d199a531 100644 --- a/node/node.go +++ b/node/node.go @@ -723,7 +723,7 @@ func (n *Node) ValidateTransaction(tx *transaction.Transaction) error { if errors.Is(err, process.ErrAccountNotFound) { return fmt.Errorf("%w for address %s", process.ErrInsufficientFunds, - n.getFeePayer(tx), + n.extractAddressFromError(err), ) } @@ -1545,12 +1545,20 @@ func (n *Node) getKeyBytes(key string) ([]byte, error) { return hex.DecodeString(key) } -func (n *Node) getFeePayer(tx *transaction.Transaction) string { - if common.IsValidRelayedTxV3(tx) { - return n.coreComponents.AddressPubKeyConverter().SilentEncode(tx.GetRelayerAddr(), log) +func (n *Node) extractAddressFromError(err error) string { + if !strings.Contains(err.Error(), "for address") { + return "" } - return n.coreComponents.AddressPubKeyConverter().SilentEncode(tx.GetSndAddr(), log) + errWords := strings.Split(err.Error(), " ") + for _, word := range errWords { + _, errDecode := n.coreComponents.AddressPubKeyConverter().Decode(word) + if errDecode == nil { + return word + } + } + + return "" } // IsInterfaceNil returns true if there is no value under the interface diff --git a/node/node_test.go b/node/node_test.go index cd064e6087b..48c1b115091 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -3016,7 +3016,7 @@ func TestValidateTransaction_ShouldAdaptAccountNotFoundError(t *testing.T) { err := n.ValidateTransaction(tx) require.Equal(t, "insufficient funds for address erd1xycnzvf3xycnzvf3xycnzvf3xycnzvf3xycnzvf3xycnzvf3xycspcqad6", err.Error()) }) - t.Run("relayed tx v3", func(t *testing.T) { + t.Run("relayed tx v3, no funds for sender", func(t *testing.T) { tx := &transaction.Transaction{ SndAddr: bytes.Repeat([]byte("1"), 32), RcvAddr: bytes.Repeat([]byte("1"), 32), @@ -3027,6 +3027,38 @@ func TestValidateTransaction_ShouldAdaptAccountNotFoundError(t *testing.T) { ChainID: []byte("chainID"), } err := n.ValidateTransaction(tx) + require.Equal(t, "insufficient funds for address erd1xycnzvf3xycnzvf3xycnzvf3xycnzvf3xycnzvf3xycnzvf3xycspcqad6", err.Error()) + }) + t.Run("relayed tx v3, no funds for relayer", func(t *testing.T) { + tx := &transaction.Transaction{ + SndAddr: bytes.Repeat([]byte("1"), 32), + RcvAddr: bytes.Repeat([]byte("1"), 32), + Value: big.NewInt(37), + Signature: []byte("sSignature"), + RelayerAddr: bytes.Repeat([]byte("2"), 32), + RelayerSignature: []byte("rSignature"), + ChainID: []byte("chainID"), + } + + stateComp := getDefaultStateComponents() + stateComp.AccountsAPI = &stateMock.AccountsStub{ + GetExistingAccountCalled: func(addressContainer []byte) (vmcommon.AccountHandler, error) { + if bytes.Equal(addressContainer, tx.SndAddr) { + return &stateMock.UserAccountStub{}, nil + } + + return nil, errors.New("account not found") + }, + } + nLocal, _ := node.NewNode( + node.WithCoreComponents(getDefaultCoreComponents()), + node.WithBootstrapComponents(getDefaultBootstrapComponents()), + node.WithProcessComponents(getDefaultProcessComponents()), + node.WithStateComponents(stateComp), + node.WithCryptoComponents(getDefaultCryptoComponents()), + ) + + err := nLocal.ValidateTransaction(tx) require.Equal(t, "insufficient funds for address erd1xgeryv3jxgeryv3jxgeryv3jxgeryv3jxgeryv3jxgeryv3jxgeqvw86cj", err.Error()) }) } From 3170be4aa43c56b9a49f16191494e110a2073f07 Mon Sep 17 00:00:00 2001 From: Sorin Stanculeanu Date: Wed, 27 Nov 2024 11:58:03 +0200 Subject: [PATCH 19/39] extra check for relayer != guardian --- process/errors.go | 3 ++ process/mock/multipleShardsCoordinatorMock.go | 6 +++- process/transaction/interceptedTransaction.go | 12 ++++++-- .../interceptedTransaction_test.go | 30 ++++++++++++++++++- process/transaction/shardProcess.go | 4 +++ process/transaction/shardProcess_test.go | 8 +++++ 6 files changed, 59 insertions(+), 4 deletions(-) diff --git a/process/errors.go b/process/errors.go index d75093d8cd9..cd508052e61 100644 --- a/process/errors.go +++ b/process/errors.go @@ -1235,3 +1235,6 @@ var ErrRelayedTxV3Disabled = errors.New("relayed tx v3 are disabled") // ErrGuardedRelayerNotAllowed signals that the provided relayer is guarded var ErrGuardedRelayerNotAllowed = errors.New("guarded relayer not allowed") + +// ErrRelayedByGuardianNotAllowed signals that the provided guardian is also the relayer +var ErrRelayedByGuardianNotAllowed = errors.New("relayed by guardian not allowed") diff --git a/process/mock/multipleShardsCoordinatorMock.go b/process/mock/multipleShardsCoordinatorMock.go index bff3e16d090..27cb599cf92 100644 --- a/process/mock/multipleShardsCoordinatorMock.go +++ b/process/mock/multipleShardsCoordinatorMock.go @@ -6,6 +6,7 @@ import ( type multipleShardsCoordinatorMock struct { ComputeIdCalled func(address []byte) uint32 + SameShardCalled func(firstAddress, secondAddress []byte) bool noShards uint32 CurrentShard uint32 } @@ -44,7 +45,10 @@ func (scm *multipleShardsCoordinatorMock) SetSelfId(_ uint32) error { } // SameShard - -func (scm *multipleShardsCoordinatorMock) SameShard(_, _ []byte) bool { +func (scm *multipleShardsCoordinatorMock) SameShard(firstAddress, secondAddress []byte) bool { + if scm.SameShardCalled != nil { + return scm.SameShardCalled(firstAddress, secondAddress) + } return true } diff --git a/process/transaction/interceptedTransaction.go b/process/transaction/interceptedTransaction.go index 9efd86c321a..b3c48a0eab2 100644 --- a/process/transaction/interceptedTransaction.go +++ b/process/transaction/interceptedTransaction.go @@ -240,7 +240,15 @@ func (inTx *InterceptedTransaction) verifyIfRelayedTxV3(tx *transaction.Transact err := inTx.integrity(tx) if err != nil { - return fmt.Errorf("inner transaction: %w", err) + return err + } + + if !inTx.coordinator.SameShard(tx.RelayerAddr, tx.SndAddr) { + return process.ErrShardIdMissmatch + } + + if bytes.Equal(tx.RelayerAddr, tx.GuardianAddr) { + return process.ErrRelayedByGuardianNotAllowed } userTx := *tx @@ -252,7 +260,7 @@ func (inTx *InterceptedTransaction) verifyIfRelayedTxV3(tx *transaction.Transact err = inTx.verifyRelayerSig(tx) if err != nil { - return fmt.Errorf("inner transaction: %w", err) + return err } return nil diff --git a/process/transaction/interceptedTransaction_test.go b/process/transaction/interceptedTransaction_test.go index 4eafa62ae8d..5a95cc15a24 100644 --- a/process/transaction/interceptedTransaction_test.go +++ b/process/transaction/interceptedTransaction_test.go @@ -171,7 +171,8 @@ func createInterceptedTxFromPlainTxWithArgParser(tx *dataTransaction.Transaction shardCoordinator := mock.NewMultipleShardsCoordinatorMock() shardCoordinator.CurrentShard = 0 shardCoordinator.ComputeIdCalled = func(address []byte) uint32 { - if bytes.Equal(address, senderAddress) { + if bytes.Equal(address, senderAddress) || + bytes.Equal(address, relayerAddress) { return senderShard } if bytes.Equal(address, recvAddress) { @@ -180,6 +181,10 @@ func createInterceptedTxFromPlainTxWithArgParser(tx *dataTransaction.Transaction return shardCoordinator.CurrentShard } + shardCoordinator.SameShardCalled = func(firstAddress, secondAddress []byte) bool { + return string(firstAddress) == string(relayerAddress) && + string(secondAddress) == string(senderAddress) + } return transaction.NewInterceptedTransaction( txBuff, @@ -1524,17 +1529,40 @@ func TestInterceptedTransaction_CheckValidityOfRelayedTxV3(t *testing.T) { tx.Signature = sigOk tx.RelayerSignature = sigOk + // sender in different shard than relayer should fail + tx.RelayerAddr = bytes.Repeat([]byte("a"), len(relayerAddress)) + txi, _ = createInterceptedTxFromPlainTxWithArgParser(tx) + err = txi.CheckValidity() + assert.Equal(t, process.ErrShardIdMissmatch, err) + + // relayer == guardian should fail + tx.Version = 2 + tx.Options = 2 + tx.RelayerAddr = relayerAddress + tx.GuardianAddr = tx.RelayerAddr + tx.GuardianSignature = sigOk + txi, _ = createInterceptedTxFromPlainTxWithArgParser(tx) + err = txi.CheckValidity() + assert.Equal(t, process.ErrRelayedByGuardianNotAllowed, err) + + // recursive relayed txs + tx.Version = minTxVersion + tx.Options = 0 + tx.GuardianAddr = nil + tx.GuardianSignature = nil tx.Data = []byte(core.RelayedTransactionV2 + "@" + hex.EncodeToString(recvAddress) + "@" + hex.EncodeToString(big.NewInt(0).SetUint64(0).Bytes()) + "@" + hex.EncodeToString([]byte("some method")) + "@" + hex.EncodeToString(sigOk)) txi, _ = createInterceptedTxFromPlainTxWithArgParser(tx) err = txi.CheckValidity() assert.True(t, errors.Is(err, process.ErrRecursiveRelayedTxIsNotAllowed)) + // invalid relayer signature tx.Data = nil tx.RelayerSignature = bytes.Repeat([]byte("a"), len(sigOk)) // same length but invalid relayer sig txi, _ = createInterceptedTxFromPlainTxWithArgParser(tx) err = txi.CheckValidity() assert.NotNil(t, err) + // should work tx.RelayerSignature = sigOk txi, _ = createInterceptedTxFromPlainTxWithArgParser(tx) err = txi.CheckValidity() diff --git a/process/transaction/shardProcess.go b/process/transaction/shardProcess.go index 8dfd7f884d3..26143d10a81 100644 --- a/process/transaction/shardProcess.go +++ b/process/transaction/shardProcess.go @@ -823,6 +823,10 @@ func (txProc *txProcessor) processRelayedTxV3(tx *transaction.Transaction) (vmco return vmcommon.UserError, txProc.executingFailedTransactionRelayedV3(tx, sndAccount, relayerAccount, process.ErrGuardedRelayerNotAllowed) } + if bytes.Equal(tx.RelayerAddr, tx.GuardianAddr) { + return vmcommon.UserError, txProc.executingFailedTransactionRelayedV3(tx, sndAccount, relayerAccount, process.ErrRelayedByGuardianNotAllowed) + } + userTx := *tx // remove relayer signature for tx type handler // hash of this user tx won't be computed/used, but the originalTxHash diff --git a/process/transaction/shardProcess_test.go b/process/transaction/shardProcess_test.go index c278b83c80b..8db2d4f21cb 100644 --- a/process/transaction/shardProcess_test.go +++ b/process/transaction/shardProcess_test.go @@ -2784,6 +2784,14 @@ func TestTxProcessor_ProcessRelayedTransactionV3(t *testing.T) { assert.Equal(t, process.ErrFailedTransaction, err) assert.Equal(t, vmcommon.UserError, returnCode) }) + t.Run("same guardian and relayer should error", func(t *testing.T) { + txCopy := *tx + txCopy.Nonce = acntSrc.GetNonce() + txCopy.GuardianAddr = txCopy.RelayerAddr + returnCode, err := txProc.ProcessTransaction(&txCopy) + assert.Equal(t, process.ErrFailedTransaction, err) + assert.Equal(t, vmcommon.UserError, returnCode) + }) t.Run("insufficient gas limit should error", func(t *testing.T) { txCopy := *tx txCopy.Nonce = acntSrc.GetNonce() From d23b95356a9d93b3c81bd4ac3b656bbf886dbc56 Mon Sep 17 00:00:00 2001 From: Sorin Stanculeanu Date: Wed, 27 Nov 2024 11:58:42 +0200 Subject: [PATCH 20/39] fix typos --- .../chainSimulator/relayedTx/relayedTx_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/integrationTests/chainSimulator/relayedTx/relayedTx_test.go b/integrationTests/chainSimulator/relayedTx/relayedTx_test.go index 63f1828d451..7d4cff0d01f 100644 --- a/integrationTests/chainSimulator/relayedTx/relayedTx_test.go +++ b/integrationTests/chainSimulator/relayedTx/relayedTx_test.go @@ -544,11 +544,11 @@ func testRelayedV3MetaInteraction() func(t *testing.T) { err = cs.GenerateBlocks(1) require.NoError(t, err) - // send relayed tx with invalid value - txDataAdd := "createNewDelegationContract@00@00" + // send createNewDelegationContract transaction + txData := "createNewDelegationContract@00@00" gasLimit := uint64(60000000) value := big.NewInt(0).Mul(oneEGLD, big.NewInt(1250)) - relayedTx := generateRelayedV3Transaction(sender.Bytes, 0, vm.DelegationManagerSCAddress, relayer.Bytes, value, txDataAdd, gasLimit) + relayedTx := generateRelayedV3Transaction(sender.Bytes, 0, vm.DelegationManagerSCAddress, relayer.Bytes, value, txData, gasLimit) relayerBefore := getBalance(t, cs, relayer) senderBefore := getBalance(t, cs, sender) @@ -565,7 +565,7 @@ func testRelayedV3MetaInteraction() func(t *testing.T) { refund := getRefundValue(result.SmartContractResults) consumedFee := big.NewInt(0).Sub(relayerBefore, relayerAfter) - gasForFullPrice := uint64(len(txDataAdd)*gasPerDataByte + minGasLimit + minGasLimit) + gasForFullPrice := uint64(len(txData)*gasPerDataByte + minGasLimit + minGasLimit) gasForDeductedPrice := gasLimit - gasForFullPrice deductedGasPrice := uint64(minGasPrice / deductionFactor) initialFee := gasForFullPrice*minGasPrice + gasForDeductedPrice*deductedGasPrice From 6ae2337bb3d7b185ff142b6e583015629f1646a2 Mon Sep 17 00:00:00 2001 From: Sorin Stanculeanu Date: Thu, 28 Nov 2024 17:26:14 +0200 Subject: [PATCH 21/39] proper execution of relayed v3 --- .../relayedTx/relayedTx_test.go | 50 +- .../smartContract/processorV2/processV2.go | 62 ++- .../smartContract/processorV2/vmInputV2.go | 11 +- process/transaction/baseProcess.go | 63 ++- process/transaction/metaProcess.go | 8 +- process/transaction/shardProcess.go | 476 ++++++++++-------- process/transaction/shardProcess_test.go | 12 +- 7 files changed, 398 insertions(+), 284 deletions(-) diff --git a/integrationTests/chainSimulator/relayedTx/relayedTx_test.go b/integrationTests/chainSimulator/relayedTx/relayedTx_test.go index 7d4cff0d01f..57c4393e1a4 100644 --- a/integrationTests/chainSimulator/relayedTx/relayedTx_test.go +++ b/integrationTests/chainSimulator/relayedTx/relayedTx_test.go @@ -146,6 +146,10 @@ func testRelayedV3MoveBalance( result, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(relayedTx, maxNumOfBlocksToGenerateWhenExecutingTx) require.NoError(t, err) + if relayerShard == destinationShard { + require.NoError(t, cs.GenerateBlocks(maxNumOfBlocksToGenerateWhenExecutingTx)) + } + // check fee fields initiallyPaidFee, fee, gasUsed := computeTxGasAndFeeBasedOnRefund(result, big.NewInt(0), true, guardedTx) require.Equal(t, initiallyPaidFee.String(), result.InitiallyPaidFee) @@ -166,25 +170,11 @@ func testRelayedV3MoveBalance( receiverBalanceAfter := getBalance(t, cs, receiver) require.Equal(t, oneEGLD.String(), receiverBalanceAfter.String()) - // check scr - require.Equal(t, 1, len(result.SmartContractResults)) - require.Equal(t, relayer.Bech32, result.SmartContractResults[0].RelayerAddr) - require.Equal(t, sender.Bech32, result.SmartContractResults[0].SndAddr) - require.Equal(t, receiver.Bech32, result.SmartContractResults[0].RcvAddr) - require.Equal(t, relayedTx.Value, result.SmartContractResults[0].Value) + // check scrs, should be none + require.Zero(t, len(result.SmartContractResults)) // check intra shard logs, should be none require.Nil(t, result.Logs) - - // check cross shard log, should be one completedTxEvent - if relayerShard == destinationShard { - return - } - scrResult, err := cs.GetNodeHandler(destinationShard).GetFacadeHandler().GetTransaction(result.SmartContractResults[0].Hash, true) - require.NoError(t, err) - require.NotNil(t, scrResult.Logs) - require.Equal(t, 1, len(scrResult.Logs.Events)) - require.Contains(t, scrResult.Logs.Events[0].Identifier, core.CompletedTxEventIdentifier) } } @@ -349,7 +339,7 @@ func testRelayedV3ScCall( require.Equal(t, feeDeploy.String(), ownerFee.String()) // check scrs - require.Equal(t, 2, len(result.SmartContractResults)) + require.Equal(t, 1, len(result.SmartContractResults)) for _, scr := range result.SmartContractResults { checkSCRSucceeded(t, cs, scr) } @@ -398,16 +388,9 @@ func testRelayedV3ScCallInvalidGasLimit( result, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(relayedTx, maxNumOfBlocksToGenerateWhenExecutingTx) require.NoError(t, err) - logs := result.Logs - // if cross shard, generate few more blocks for cross shard scrs - if relayerShard != ownerShard { - require.NoError(t, cs.GenerateBlocks(maxNumOfBlocksToGenerateWhenExecutingTx)) - logs = result.SmartContractResults[0].Logs - } - - require.NotNil(t, logs) - require.Equal(t, 2, len(logs.Events)) - for _, event := range logs.Events { + require.NotNil(t, result.Logs) + require.Equal(t, 2, len(result.Logs.Events)) + for _, event := range result.Logs.Events { if event.Identifier == core.SignalErrorOperation { continue } @@ -480,16 +463,9 @@ func testRelayedV3ScCallInvalidMethod( result, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(relayedTx, maxNumOfBlocksToGenerateWhenExecutingTx) require.NoError(t, err) - logs := result.Logs - // if cross shard, generate few more blocks for cross shard scrs - if relayerShard != ownerShard { - require.NoError(t, cs.GenerateBlocks(maxNumOfBlocksToGenerateWhenExecutingTx)) - logs = result.SmartContractResults[0].Logs - } - - require.NotNil(t, logs) - require.Equal(t, 2, len(logs.Events)) - for _, event := range logs.Events { + require.NotNil(t, result.Logs) + require.Equal(t, 2, len(result.Logs.Events)) + for _, event := range result.Logs.Events { if event.Identifier == core.SignalErrorOperation { continue } diff --git a/process/smartContract/processorV2/processV2.go b/process/smartContract/processorV2/processV2.go index 5f6c02b7d09..e34f0ac72aa 100644 --- a/process/smartContract/processorV2/processV2.go +++ b/process/smartContract/processorV2/processV2.go @@ -603,6 +603,9 @@ func (sc *scProcessor) updateDeveloperRewards( } moveBalanceGasLimit := sc.economicsFee.ComputeGasLimit(tx) + if common.IsValidRelayedTxV3(tx) { + moveBalanceGasLimit -= sc.economicsFee.MinGasLimit() // this was already consumed from the relayer + } if !isSmartContractResult(tx) { usedGasByMainSC, err = core.SafeSubUint64(usedGasByMainSC, moveBalanceGasLimit) if err != nil { @@ -744,6 +747,9 @@ func (sc *scProcessor) computeTotalConsumedFeeAndDevRwd( } moveBalanceGasLimit := sc.economicsFee.ComputeGasLimit(tx) + if common.IsValidRelayedTxV3(tx) { + moveBalanceGasLimit -= sc.economicsFee.MinGasLimit() // this was already consumed from the relayer + } if !isSmartContractResult(tx) { displayConsumedGas := consumedGas consumedGas, err = core.SafeSubUint64(consumedGas, moveBalanceGasLimit) @@ -1408,7 +1414,7 @@ func (sc *scProcessor) getOriginalTxHashIfIntraShardRelayedSCR( tx data.TransactionHandler, txHash []byte, ) []byte { - relayedSCR, isRelayed := isRelayedTx(tx) + relayedSCR, isRelayed := isRelayedSCR(tx) if !isRelayed { return txHash } @@ -1578,7 +1584,7 @@ func (sc *scProcessor) processForRelayerWhenError( txHash []byte, returnMessage []byte, ) (*vmcommon.LogEntry, error) { - relayedSCR, isRelayed := isRelayedTx(originalTx) + relayedSCR, isRelayed := isRelayedSCR(originalTx) if !isRelayed { return nil, nil } @@ -1664,7 +1670,7 @@ func createNewLogFromSCRIfError(txHandler data.TransactionHandler) *vmcommon.Log } // transaction must be of type SCR and relayed address to be set with relayed value higher than 0 -func isRelayedTx(tx data.TransactionHandler) (*smartContractResult.SmartContractResult, bool) { +func isRelayedSCR(tx data.TransactionHandler) (*smartContractResult.SmartContractResult, bool) { relayedSCR, ok := tx.(*smartContractResult.SmartContractResult) if !ok { return nil, false @@ -1686,7 +1692,7 @@ func (sc *scProcessor) addBackTxValues( ) error { valueForSnd := big.NewInt(0).Set(scrIfError.Value) - relayedSCR, isRelayed := isRelayedTx(originalTx) + relayedSCR, isRelayed := isRelayedSCR(originalTx) if isRelayed { valueForSnd.Sub(valueForSnd, relayedSCR.RelayedValue) if valueForSnd.Cmp(zero) < 0 { @@ -1924,6 +1930,11 @@ func (sc *scProcessor) processSCPayment(tx data.TransactionHandler, acntSnd stat cost := sc.economicsFee.ComputeTxFee(tx) cost = cost.Add(cost, tx.GetValue()) + if common.IsValidRelayedTxV3(tx) { + // for relayed v3, fee was consumed from relayer + cost = tx.GetValue() + } + if cost.Cmp(big.NewInt(0)) == 0 { return nil } @@ -2257,12 +2268,18 @@ func createBaseSCR( result.CallType = vmData.DirectCall setOriginalTxHash(result, txHash, tx) - relayedTx, isRelayed := isRelayedTx(tx) + relayedTx, isRelayed := isRelayedSCR(tx) if isRelayed { result.RelayedValue = big.NewInt(0) result.RelayerAddr = relayedTx.RelayerAddr } + if common.IsValidRelayedTxV3(tx) { + relayedTx := tx.(data.RelayedTransactionHandler) + result.RelayedValue = big.NewInt(0) + result.RelayerAddr = relayedTx.GetRelayerAddr() + } + return result } @@ -2299,12 +2316,18 @@ func (sc *scProcessor) createAsyncCallBackSCRFromVMOutput( OriginalSender: origScr.GetOriginalSender(), } setOriginalTxHash(scr, txHash, tx) - relayedTx, isRelayed := isRelayedTx(tx) + relayedTx, isRelayed := isRelayedSCR(tx) if isRelayed { scr.RelayedValue = big.NewInt(0) scr.RelayerAddr = relayedTx.RelayerAddr } + if common.IsValidRelayedTxV3(tx) { + relayedTx := tx.(data.RelayedTransactionHandler) + scr.RelayedValue = big.NewInt(0) + scr.RelayerAddr = relayedTx.GetRelayerAddr() + } + sc.addVMOutputResultsToSCR(vmOutput, scr) var err error @@ -2569,7 +2592,7 @@ func (sc *scProcessor) createSCRForSenderAndRelayer( } var refundGasToRelayerSCR *smartContractResult.SmartContractResult - relayedSCR, isRelayed := isRelayedTx(tx) + relayedSCR, isRelayed := isRelayedSCR(tx) shouldRefundGasToRelayerSCR := isRelayed && callType != vmData.AsynchronousCall && gasRefund.Cmp(zero) > 0 if shouldRefundGasToRelayerSCR { senderForRelayerRefund := tx.GetRcvAddr() @@ -2592,6 +2615,31 @@ func (sc *scProcessor) createSCRForSenderAndRelayer( gasRemaining = 0 } + isRelayedV3 := common.IsValidRelayedTxV3(tx) + shouldRefundGasToRelayerSCR = isRelayedV3 && callType != vmData.AsynchronousCall && gasRefund.Cmp(zero) > 0 + if shouldRefundGasToRelayerSCR { + senderForRelayerRefund := tx.GetRcvAddr() + if !sc.isSelfShard(tx.GetRcvAddr()) { + senderForRelayerRefund = tx.GetSndAddr() + } + + relayedTx := tx.(data.RelayedTransactionHandler) + + refundGasToRelayerSCR = &smartContractResult.SmartContractResult{ + Nonce: tx.GetNonce() + 1, + Value: big.NewInt(0).Set(gasRefund), + RcvAddr: relayedTx.GetRelayerAddr(), + SndAddr: senderForRelayerRefund, + PrevTxHash: txHash, + OriginalTxHash: txHash, + GasPrice: tx.GetGasPrice(), + CallType: vmData.DirectCall, + ReturnMessage: []byte("gas refund for relayer"), + OriginalSender: tx.GetSndAddr(), + } + gasRemaining = 0 + } + scTx := &smartContractResult.SmartContractResult{} scTx.Value = big.NewInt(0).Set(storageFreeRefund) if callType != vmData.AsynchronousCall && check.IfNil(refundGasToRelayerSCR) { diff --git a/process/smartContract/processorV2/vmInputV2.go b/process/smartContract/processorV2/vmInputV2.go index 06c4c3f0ad2..44b20b1bcc3 100644 --- a/process/smartContract/processorV2/vmInputV2.go +++ b/process/smartContract/processorV2/vmInputV2.go @@ -7,6 +7,7 @@ import ( "github.com/multiversx/mx-chain-core-go/data" "github.com/multiversx/mx-chain-core-go/data/smartContractResult" "github.com/multiversx/mx-chain-core-go/data/vm" + "github.com/multiversx/mx-chain-go/common" "github.com/multiversx/mx-chain-go/process" vmcommon "github.com/multiversx/mx-chain-vm-common-go" ) @@ -40,10 +41,14 @@ func (sc *scProcessor) initializeVMInputFromTx(vmInput *vmcommon.VMInput, tx dat vmInput.CallValue = new(big.Int).Set(tx.GetValue()) vmInput.GasPrice = tx.GetGasPrice() - relayedTx, isRelayed := isRelayedTx(tx) + relayedTx, isRelayed := isRelayedSCR(tx) if isRelayed { vmInput.RelayerAddr = relayedTx.RelayerAddr } + if common.IsValidRelayedTxV3(tx) { + relayedTx := tx.(data.RelayedTransactionHandler) + vmInput.RelayerAddr = relayedTx.GetRelayerAddr() + } vmInput.GasProvided, err = sc.prepareGasProvided(tx) if err != nil { @@ -68,6 +73,10 @@ func (sc *scProcessor) prepareGasProvided(tx data.TransactionHandler) (uint64, e } gasForTxData := sc.economicsFee.ComputeGasLimit(tx) + if common.IsValidRelayedTxV3(tx) { + gasForTxData -= sc.economicsFee.MinGasLimit() // this was already consumed from the relayer + } + if tx.GetGasLimit() < gasForTxData { return 0, process.ErrNotEnoughGas } diff --git a/process/transaction/baseProcess.go b/process/transaction/baseProcess.go index fec873a0fa6..9aecdd534d4 100644 --- a/process/transaction/baseProcess.go +++ b/process/transaction/baseProcess.go @@ -119,6 +119,15 @@ func (txProc *baseTxProcessor) checkTxValues( acntSnd, acntDst state.UserAccountHandler, isUserTxOfRelayed bool, ) error { + if common.IsValidRelayedTxV3(tx) { + relayerAccount, _, err := txProc.getAccounts(tx.RelayerAddr, tx.RelayerAddr) + if err != nil { + return err + } + + return txProc.checkUserTxOfRelayedV3Values(tx, acntSnd, acntDst, relayerAccount) + } + err := txProc.verifyGuardian(tx, acntSnd) if err != nil { return err @@ -152,15 +161,10 @@ func (txProc *baseTxProcessor) checkTxValues( txFee = txProc.economicsFee.ComputeTxFee(tx) } - feePayer, isRelayedV3, err := txProc.getFeePayer(tx, acntSnd) - if err != nil { - return err - } - - if feePayer.GetBalance().Cmp(txFee) < 0 { + if acntSnd.GetBalance().Cmp(txFee) < 0 { return fmt.Errorf("%w, has: %s, wanted: %s", process.ErrInsufficientFee, - feePayer.GetBalance().String(), + acntSnd.GetBalance().String(), txFee.String(), ) } @@ -171,16 +175,8 @@ func (txProc *baseTxProcessor) checkTxValues( txFee = core.SafeMul(tx.GasLimit, tx.GasPrice) } - // early exit for relayed v3. This check is done on the relayed transaction, thus - // the fee payer at this point should be the relayer. - // The check for the cost (fee + value), will be done later on, as part of checkUserTxOfRelayedV3Values - // on the sender account, after relayed moved the fee. - if isRelayedV3 { - return nil - } - cost := big.NewInt(0).Add(txFee, tx.Value) - if feePayer.GetBalance().Cmp(cost) < 0 { + if acntSnd.GetBalance().Cmp(cost) < 0 { return process.ErrInsufficientFunds } @@ -189,23 +185,25 @@ func (txProc *baseTxProcessor) checkTxValues( func (txProc *baseTxProcessor) checkUserTxOfRelayedV3Values( tx *transaction.Transaction, - acntSnd, acntDst state.UserAccountHandler, + senderAccount state.UserAccountHandler, + destinationAccount state.UserAccountHandler, + relayerAccount state.UserAccountHandler, ) error { - err := txProc.verifyGuardian(tx, acntSnd) + err := txProc.verifyGuardian(tx, senderAccount) if err != nil { return err } - err = txProc.checkUserNames(tx, acntSnd, acntDst) + err = txProc.checkUserNames(tx, senderAccount, destinationAccount) if err != nil { return err } - if check.IfNil(acntSnd) { + if check.IfNil(senderAccount) { return nil } - if acntSnd.GetNonce() < tx.Nonce { + if senderAccount.GetNonce() < tx.Nonce { return process.ErrHigherNonceInTransaction } - if acntSnd.GetNonce() > tx.Nonce { + if senderAccount.GetNonce() > tx.Nonce { return process.ErrLowerNonceInTransaction } err = txProc.economicsFee.CheckValidityTxValues(tx) @@ -214,27 +212,24 @@ func (txProc *baseTxProcessor) checkUserTxOfRelayedV3Values( } if tx.GasLimit < txProc.economicsFee.ComputeGasLimit(tx) { - return process.ErrNotEnoughGasInUserTx + return process.ErrNotEnoughGas } - txFee := txProc.computeInnerTxFee(tx) + if check.IfNil(relayerAccount) { + return nil + } - if acntSnd.GetBalance().Cmp(txFee) < 0 { + txFee := txProc.economicsFee.ComputeTxFee(tx) + + if relayerAccount.GetBalance().Cmp(txFee) < 0 { return fmt.Errorf("%w, has: %s, wanted: %s", process.ErrInsufficientFee, - acntSnd.GetBalance().String(), + relayerAccount.GetBalance().String(), txFee.String(), ) } - if !txProc.enableEpochsHandler.IsFlagEnabled(common.PenalizedTooMuchGasFlag) { - // backwards compatibility issue when provided gas limit and gas price exceeds the available balance before the - // activation of the "penalize too much gas" flag - txFee = core.SafeMul(tx.GasLimit, tx.GasPrice) - } - - cost := big.NewInt(0).Add(txFee, tx.Value) - if acntSnd.GetBalance().Cmp(cost) < 0 { + if senderAccount.GetBalance().Cmp(tx.Value) < 0 { return process.ErrInsufficientFunds } diff --git a/process/transaction/metaProcess.go b/process/transaction/metaProcess.go index bf3bceb2780..577b9c963fb 100644 --- a/process/transaction/metaProcess.go +++ b/process/transaction/metaProcess.go @@ -135,7 +135,13 @@ func (txProc *metaTxProcessor) ProcessTransaction(tx *transaction.Transaction) ( return 0, err } + txCopy := *tx txType, _ := txProc.txTypeHandler.ComputeTransactionType(tx) + if txType == process.RelayedTxV3 { + // extract the inner transaction in order to get the proper user tx type + txCopy.RelayerSignature = nil + txType, _ = txProc.txTypeHandler.ComputeTransactionType(&txCopy) + } switch txType { case process.SCDeployment: return txProc.processSCDeployment(tx, tx.SndAddr) @@ -145,8 +151,6 @@ func (txProc *metaTxProcessor) ProcessTransaction(tx *transaction.Transaction) ( if txProc.enableEpochsHandler.IsFlagEnabled(common.ESDTFlag) { return txProc.processSCInvoking(tx, tx.SndAddr, tx.RcvAddr) } - case process.RelayedTxV3: - return vmcommon.Ok, nil // it will be processed through the scr created on source } snapshot := txProc.accounts.JournalLen() diff --git a/process/transaction/shardProcess.go b/process/transaction/shardProcess.go index 26143d10a81..5091fe71ba6 100644 --- a/process/transaction/shardProcess.go +++ b/process/transaction/shardProcess.go @@ -297,8 +297,13 @@ func (txProc *txProcessor) executingFailedTransaction( return nil } + feePayer, isRelayedV3, err := txProc.getFeePayer(tx, acntSnd) + if err != nil { + return err + } + txFee := txProc.economicsFee.ComputeTxFee(tx) - err := acntSnd.SubFromBalance(txFee) + err = feePayer.SubFromBalance(txFee) if err != nil { return err } @@ -330,11 +335,19 @@ func (txProc *txProcessor) executingFailedTransaction( txProc.txFeeHandler.ProcessTransactionFee(txFee, big.NewInt(0), txHash) - err = txProc.accounts.SaveAccount(acntSnd) + err = txProc.accounts.SaveAccount(feePayer) if err != nil { return err } + if isRelayedV3 { + // for relayed v3, the nonce was increased for sender, but fees consumed from relayer + err = txProc.accounts.SaveAccount(acntSnd) + if err != nil { + return err + } + } + return process.ErrFailedTransaction } @@ -454,6 +467,11 @@ func (txProc *txProcessor) processTxFee( return nil, nil, err } + err = txProc.accounts.SaveAccount(acntSnd) + if err != nil { + return nil, nil, err + } + if dstShardTxType == process.MoveBalance { return totalCost, totalCost, nil } @@ -482,22 +500,77 @@ func (txProc *txProcessor) processTxFee( if err != nil { return nil, nil, err } + + err = txProc.accounts.SaveAccount(acntSnd) + if err != nil { + return nil, nil, err + } } else { err := acntSnd.SubFromBalance(moveBalanceFee) if err != nil { return nil, nil, err } + + err = txProc.accounts.SaveAccount(acntSnd) + if err != nil { + return nil, nil, err + } } return moveBalanceFee, totalCost, nil } -func (txProc *txProcessor) checkIfValidTxToMetaChain( +func (txProc *txProcessor) processTxFeeOfRelayedV3( tx *transaction.Transaction, - adrDst []byte, + txHash []byte, + relayerAccount state.UserAccountHandler, + destinationAccount state.UserAccountHandler, + dstShardTxType process.TransactionType, ) error { + if check.IfNil(relayerAccount) { + return nil + } + + moveBalanceFee := txProc.economicsFee.ComputeMoveBalanceFee(tx) + totalCost := txProc.economicsFee.ComputeTxFee(tx) + + if !txProc.enableEpochsHandler.IsFlagEnabled(common.PenalizedTooMuchGasFlag) { + totalCost = core.SafeMul(tx.GasLimit, tx.GasPrice) + } + + isCrossShardSCCall := check.IfNil(destinationAccount) && len(tx.GetData()) > 0 && core.IsSmartContractAddress(tx.GetRcvAddr()) + if dstShardTxType != process.MoveBalance || + (!txProc.enableEpochsHandler.IsFlagEnabled(common.MetaProtectionFlag) && isCrossShardSCCall) { + + err := relayerAccount.SubFromBalance(totalCost) + if err != nil { + return err + } + + err = txProc.accounts.SaveAccount(relayerAccount) + if err != nil { + return err + } + } else { + err := relayerAccount.SubFromBalance(moveBalanceFee) + if err != nil { + return err + } + + txProc.txFeeHandler.ProcessTransactionFee(moveBalanceFee, big.NewInt(0), txHash) + + err = txProc.accounts.SaveAccount(relayerAccount) + if err != nil { + return err + } + } - destShardId := txProc.shardCoordinator.ComputeId(adrDst) + return nil +} + +func (txProc *txProcessor) checkIfValidTxToMetaChain(tx *transaction.Transaction) error { + + destShardId := txProc.shardCoordinator.ComputeId(tx.RcvAddr) if destShardId != core.MetachainShardId { return nil } @@ -562,7 +635,7 @@ func (txProc *txProcessor) processMoveBalance( return process.ErrAccountNotPayable } - err = txProc.checkIfValidTxToMetaChain(tx, tx.RcvAddr) + err = txProc.checkIfValidTxToMetaChain(tx) if err != nil { errLocal := txProc.revertConsumedValueFromSender(tx, acntSrc, isUserTxOfRelayed) if errLocal != nil { @@ -607,6 +680,76 @@ func (txProc *txProcessor) processMoveBalance( return nil } +func (txProc *txProcessor) processMoveBalanceOfRelayedV3( + tx *transaction.Transaction, + senderAccount state.UserAccountHandler, + destinationAccount state.UserAccountHandler, + relayerAccount state.UserAccountHandler, + destShardTxType process.TransactionType, + originalTxHash []byte, +) error { + err := txProc.processTxFeeOfRelayedV3(tx, originalTxHash, relayerAccount, destinationAccount, destShardTxType) + if err != nil { + return err + } + + // is sender address in node shard + if !check.IfNil(senderAccount) { + senderAccount.IncreaseNonce(1) + err = senderAccount.SubFromBalance(tx.Value) + if err != nil { + return err + } + + err = txProc.accounts.SaveAccount(senderAccount) + if err != nil { + return err + } + } + + isPayable, err := txProc.scProcessor.IsPayable(tx.SndAddr, tx.RcvAddr) + if err != nil { + errRefund := txProc.revertConsumedValueFromSender(tx, senderAccount, true) + if errRefund != nil { + log.Error("failed to return funds to sender after check if receiver is payable", "error", errRefund) + } + return err + } + if !isPayable { + err = txProc.revertConsumedValueFromSender(tx, senderAccount, true) + if err != nil { + log.Error("failed to return funds to sender while transferring to non payable sc", "error", err) + } + + return process.ErrAccountNotPayable + } + + err = txProc.checkIfValidTxToMetaChain(tx) + if err != nil { + errLocal := txProc.revertConsumedValueFromSender(tx, senderAccount, true) + if errLocal != nil { + log.Error("failed to return funds to sender while sending invalid tx to metachain", "error", errLocal) + } + + return err + } + + // is receiver address in node shard + if !check.IfNil(destinationAccount) { + err = destinationAccount.AddToBalance(tx.Value) + if err != nil { + return err + } + + err = txProc.accounts.SaveAccount(destinationAccount) + if err != nil { + return err + } + } + + return nil +} + func (txProc *txProcessor) revertConsumedValueFromSender( tx *transaction.Transaction, acntSrc state.UserAccountHandler, @@ -664,34 +807,85 @@ func makeUserTxFromRelayedTxV2Args(args [][]byte) *transaction.Transaction { } func (txProc *txProcessor) finishExecutionOfRelayedTxV3( - relayerAcnt, acntDst state.UserAccountHandler, + relayerAccount state.UserAccountHandler, tx *transaction.Transaction, userTx *transaction.Transaction, ) (vmcommon.ReturnCode, error) { - computedFees := txProc.computeRelayedTxV3Fees(tx, userTx) - txHash, err := txProc.consumeFeeFromRelayer( - relayerAcnt, - computedFees.totalFee, - computedFees.relayerFee, - tx, - big.NewInt(0)) + txHash, err := core.CalculateHash(txProc.marshalizer, txProc.hasher, tx) if err != nil { return 0, err } - if check.IfNil(acntDst) { - return vmcommon.Ok, nil + senderAccount, destinationAccount, err := txProc.getAccounts(tx.SndAddr, tx.RcvAddr) + if err != nil { + errRemove := txProc.increaseSenderNonceIfNeededAndLog(txHash, tx, senderAccount, err) + if errRemove != nil { + return vmcommon.UserError, errRemove + } + return vmcommon.UserError, txProc.executeFailedRelayedV3UserTx( + tx, + txHash, + err.Error()) } - err = txProc.addFeeAndValueToDest(acntDst, big.NewInt(0), computedFees.remainingFee) - if err != nil { - return 0, err + txType, dstShardTxType := txProc.txTypeHandler.ComputeTransactionType(userTx) + returnCode := vmcommon.Ok + switch txType { + case process.MoveBalance: + err = txProc.processMoveBalanceOfRelayedV3(tx, senderAccount, destinationAccount, relayerAccount, dstShardTxType, txHash) + case process.SCDeployment: + err = txProc.processTxFeeOfRelayedV3(tx, txHash, relayerAccount, destinationAccount, dstShardTxType) + if err != nil { + break + } + + returnCode, err = txProc.scProcessor.DeploySmartContract(tx, destinationAccount) + case process.SCInvoking: + err = txProc.processTxFeeOfRelayedV3(tx, txHash, relayerAccount, destinationAccount, dstShardTxType) + if err != nil { + break + } + + returnCode, err = txProc.scProcessor.ExecuteSmartContractTransaction(tx, senderAccount, destinationAccount) + case process.BuiltInFunctionCall: + err = txProc.processTxFeeOfRelayedV3(tx, txHash, relayerAccount, destinationAccount, dstShardTxType) + if err != nil { + break + } + + returnCode, err = txProc.scProcessor.ExecuteBuiltInFunction(tx, senderAccount, destinationAccount) + default: + err = process.ErrWrongTransaction + errRemove := txProc.increaseSenderNonceIfNeededAndLog(txHash, tx, senderAccount, err) + if errRemove != nil { + return vmcommon.UserError, errRemove + } + return vmcommon.UserError, txProc.executeFailedRelayedV3UserTx( + tx, + txHash, + err.Error()) + } + + if errors.Is(err, process.ErrInvalidMetaTransaction) || errors.Is(err, process.ErrAccountNotPayable) { + return vmcommon.UserError, txProc.executeFailedRelayedV3UserTx( + tx, + txHash, + err.Error()) + } + + if errors.Is(err, process.ErrFailedTransaction) { + // in case of failed inner user tx transaction we should just simply return execution failed and + // not failed transaction - as the actual transaction (the relayed we correctly executed) and thus + // it should not lend in the invalid miniblock + return vmcommon.ExecutionFailed, nil } - relayedNonce := tx.Nonce - relayerAddr := tx.RelayerAddr + if err != nil { + log.Error("processUserTx", "protocolError", err) + return vmcommon.ExecutionFailed, err + } - return txProc.processUserTxOfRelayedV3(tx, userTx, relayedNonce, relayerAddr, txHash) + return returnCode, err } func (txProc *txProcessor) finishExecutionOfRelayedTx( @@ -757,41 +951,11 @@ func (txProc *txProcessor) processTxAtRelayer( return txHash, nil } -func (txProc *txProcessor) consumeFeeFromRelayer( - relayerAcnt state.UserAccountHandler, - totalFee *big.Int, - relayerFee *big.Int, - tx *transaction.Transaction, - valueToSubFromRelayer *big.Int, -) ([]byte, error) { - txHash, err := core.CalculateHash(txProc.marshalizer, txProc.hasher, tx) - if err != nil { - return nil, err - } - - if !check.IfNil(relayerAcnt) { - err = relayerAcnt.SubFromBalance(valueToSubFromRelayer) - if err != nil { - return nil, err - } - - err = relayerAcnt.SubFromBalance(totalFee) - if err != nil { - return nil, err - } - - err = txProc.accounts.SaveAccount(relayerAcnt) - if err != nil { - return nil, err - } - - txProc.txFeeHandler.ProcessTransactionFee(relayerFee, big.NewInt(0), txHash) +func (txProc *txProcessor) addFeeAndValueToDest(acntDst state.UserAccountHandler, txValue *big.Int, remainingFee *big.Int) error { + if check.IfNil(acntDst) { + return nil } - return txHash, nil -} - -func (txProc *txProcessor) addFeeAndValueToDest(acntDst state.UserAccountHandler, txValue *big.Int, remainingFee *big.Int) error { err := acntDst.AddToBalance(txValue) if err != nil { return err @@ -837,7 +1001,7 @@ func (txProc *txProcessor) processRelayedTxV3(tx *transaction.Transaction) (vmco return vmcommon.UserError, txProc.executingFailedTransaction(tx, relayerAccount, process.ErrInsufficientGasLimitInTx) } - return txProc.finishExecutionOfRelayedTxV3(relayerAccount, sndAccount, tx, &userTx) + return txProc.finishExecutionOfRelayedTxV3(relayerAccount, tx, &userTx) } func (txProc *txProcessor) processRelayedTxV2( @@ -926,23 +1090,6 @@ func (txProc *txProcessor) computeRelayedTxFees(tx, userTx *transaction.Transact return computedFees } -func (txProc *txProcessor) computeRelayedTxV3Fees(tx, userTx *transaction.Transaction) relayedFees { - relayerGas := txProc.economicsFee.MinGasLimit() - relayerFee := core.SafeMul(relayerGas, tx.GasPrice) - - userFee := txProc.computeInnerTxFeeAfterBaseCostFix(userTx) - - totalFee := big.NewInt(0).Add(relayerFee, userFee) - - computedFees := relayedFees{ - totalFee: totalFee, - remainingFee: userFee, - relayerFee: relayerFee, - } - - return computedFees -} - func (txProc *txProcessor) removeValueAndConsumedFeeFromUser( userTx *transaction.Transaction, relayedTxValue *big.Int, @@ -986,6 +1133,28 @@ func (txProc *txProcessor) removeValueAndConsumedFeeFromUser( return nil } +func (txProc *txProcessor) increaseSenderNonceIfNeededAndLog( + originalTxHash []byte, + originalTx *transaction.Transaction, + senderAccount vmcommon.AccountHandler, + executionErr error, +) error { + if txProc.shouldIncreaseNonce(executionErr) { + if check.IfNil(senderAccount) { + return process.ErrNilUserAccount + } + + senderAccount.IncreaseNonce(1) + + err := txProc.accounts.SaveAccount(senderAccount) + if err != nil { + return err + } + } + + return txProc.addNonExecutableLog(executionErr, originalTxHash, originalTx) +} + func (txProc *txProcessor) addNonExecutableLog(executionErr error, originalTxHash []byte, originalTx data.TransactionHandler) error { if !isNonExecutableError(executionErr) { return nil @@ -1146,130 +1315,6 @@ func (txProc *txProcessor) processUserTx( return vmcommon.Ok, nil } -func (txProc *txProcessor) processUserTxOfRelayedV3( - originalTx *transaction.Transaction, - userTx *transaction.Transaction, - relayedNonce uint64, - relayerAddr []byte, - originalTxHash []byte, -) (vmcommon.ReturnCode, error) { - - acntSnd, acntDst, err := txProc.getAccounts(userTx.SndAddr, userTx.RcvAddr) - if err != nil { - errRemove := txProc.removeValueAndConsumedFeeFromUser(userTx, big.NewInt(0), originalTxHash, originalTx, err) - if errRemove != nil { - return vmcommon.UserError, errRemove - } - return vmcommon.UserError, txProc.executeFailedRelayedUserTx( - userTx, - relayerAddr, - big.NewInt(0), - relayedNonce, - originalTx, - originalTxHash, - err.Error()) - } - - txType, dstShardTxType := txProc.txTypeHandler.ComputeTransactionType(userTx) - err = txProc.checkUserTxOfRelayedV3Values(userTx, acntSnd, acntDst) - if err != nil { - errRemove := txProc.removeValueAndConsumedFeeFromUser(userTx, big.NewInt(0), originalTxHash, originalTx, err) - if errRemove != nil { - return vmcommon.UserError, errRemove - } - return vmcommon.UserError, txProc.executeFailedRelayedUserTx( - userTx, - relayerAddr, - big.NewInt(0), - relayedNonce, - originalTx, - originalTxHash, - err.Error()) - } - - scrFromTx, err := txProc.makeSCRFromUserTx(userTx, relayerAddr, big.NewInt(0), originalTxHash) - if err != nil { - return 0, err - } - - returnCode := vmcommon.Ok - switch txType { - case process.MoveBalance: - err = txProc.processMoveBalance(userTx, acntSnd, acntDst, dstShardTxType, originalTxHash, true, true) - case process.SCDeployment: - err = txProc.processMoveBalanceCostRelayedUserTx(userTx, scrFromTx, acntSnd, originalTxHash) - if err != nil { - break - } - - returnCode, err = txProc.scProcessor.DeploySmartContract(scrFromTx, acntSnd) - case process.SCInvoking: - err = txProc.processMoveBalanceCostRelayedUserTx(userTx, scrFromTx, acntSnd, originalTxHash) - if err != nil { - break - } - - returnCode, err = txProc.scProcessor.ExecuteSmartContractTransaction(scrFromTx, acntSnd, acntDst) - case process.BuiltInFunctionCall: - err = txProc.processMoveBalanceCostRelayedUserTx(userTx, scrFromTx, acntSnd, originalTxHash) - if err != nil { - break - } - - returnCode, err = txProc.scProcessor.ExecuteBuiltInFunction(scrFromTx, acntSnd, acntDst) - default: - err = process.ErrWrongTransaction - errRemove := txProc.removeValueAndConsumedFeeFromUser(userTx, big.NewInt(0), originalTxHash, originalTx, err) - if errRemove != nil { - return vmcommon.UserError, errRemove - } - return vmcommon.UserError, txProc.executeFailedRelayedUserTx( - userTx, - relayerAddr, - big.NewInt(0), - relayedNonce, - originalTx, - originalTxHash, - err.Error()) - } - - if errors.Is(err, process.ErrInvalidMetaTransaction) || errors.Is(err, process.ErrAccountNotPayable) { - return vmcommon.UserError, txProc.executeFailedRelayedUserTx( - userTx, - relayerAddr, - big.NewInt(0), - relayedNonce, - originalTx, - originalTxHash, - err.Error()) - } - - if errors.Is(err, process.ErrFailedTransaction) { - // in case of failed inner user tx transaction we should just simply return execution failed and - // not failed transaction - as the actual transaction (the relayed we correctly executed) and thus - // it should not lend in the invalid miniblock - return vmcommon.ExecutionFailed, nil - } - - if err != nil { - log.Error("processUserTx", "protocolError", err) - return vmcommon.ExecutionFailed, err - } - - // no need to add the smart contract result From TX to the intermediate transactions in case of error - // returning value is resolved inside smart contract processor or above by executeFailedRelayedUserTx - if returnCode != vmcommon.Ok { - return returnCode, nil - } - - err = txProc.scrForwarder.AddIntermediateTransactions([]data.TransactionHandler{scrFromTx}, originalTxHash) - if err != nil { - return 0, err - } - - return vmcommon.Ok, nil -} - func (txProc *baseTxProcessor) isCrossTxFromMe(adrSrc, adrDst []byte) bool { shardForSrc := txProc.shardCoordinator.ComputeId(adrSrc) shardForDst := txProc.shardCoordinator.ComputeId(adrDst) @@ -1384,6 +1429,43 @@ func (txProc *txProcessor) executeFailedRelayedUserTx( return nil } +func (txProc *txProcessor) executeFailedRelayedV3UserTx( + originalTx *transaction.Transaction, + originalTxHash []byte, + errorMsg string, +) error { + + scrForRelayer := &smartContractResult.SmartContractResult{ + Nonce: originalTx.Nonce, + Value: big.NewInt(0), + RcvAddr: originalTx.RelayerAddr, + SndAddr: originalTx.SndAddr, + PrevTxHash: originalTxHash, + OriginalTxHash: originalTxHash, + ReturnMessage: []byte(errorMsg), + } + + err := txProc.scrForwarder.AddIntermediateTransactions([]data.TransactionHandler{scrForRelayer}, originalTxHash) + if err != nil { + return err + } + + moveBalanceGasLimit := txProc.economicsFee.ComputeGasLimit(originalTx) + gasToUse := originalTx.GetGasLimit() - moveBalanceGasLimit + processingUserFee := txProc.economicsFee.ComputeFeeForProcessing(originalTx, gasToUse) + moveBalanceUserFee := txProc.economicsFee.ComputeMoveBalanceFee(originalTx) + totalFee := big.NewInt(0).Add(moveBalanceUserFee, processingUserFee) + + senderShardID := txProc.shardCoordinator.ComputeId(originalTx.SndAddr) + if senderShardID != txProc.shardCoordinator.SelfId() { + totalFee.Sub(totalFee, processingUserFee) + } + + txProc.txFeeHandler.ProcessTransactionFee(totalFee, big.NewInt(0), originalTxHash) + + return txProc.badTxForwarder.AddIntermediateTransactions([]data.TransactionHandler{originalTx}, originalTxHash) +} + func (txProc *txProcessor) shouldIncreaseNonce(executionErr error) bool { if !txProc.enableEpochsHandler.IsFlagEnabled(common.RelayedNonceFixFlag) { return true diff --git a/process/transaction/shardProcess_test.go b/process/transaction/shardProcess_test.go index 8db2d4f21cb..f65aadffb5f 100644 --- a/process/transaction/shardProcess_test.go +++ b/process/transaction/shardProcess_test.go @@ -923,7 +923,7 @@ func TestTxProcessor_ProcessMoveBalanceToSmartPayableContract(t *testing.T) { _, err := execTx.ProcessTransaction(&tx) assert.Nil(t, err) - assert.Equal(t, 2, saveAccountCalled) + assert.Equal(t, 3, saveAccountCalled) } func testProcessCheck(t *testing.T, nonce uint64, value *big.Int) { @@ -989,7 +989,7 @@ func TestTxProcessor_ProcessMoveBalancesShouldWork(t *testing.T) { _, err := execTx.ProcessTransaction(&tx) assert.Nil(t, err) - assert.Equal(t, 2, saveAccountCalled) + assert.Equal(t, 3, saveAccountCalled) } func TestTxProcessor_ProcessOkValsShouldWork(t *testing.T) { @@ -1025,7 +1025,7 @@ func TestTxProcessor_ProcessOkValsShouldWork(t *testing.T) { assert.Equal(t, uint64(5), acntSrc.GetNonce()) assert.Equal(t, big.NewInt(29), acntSrc.GetBalance()) assert.Equal(t, big.NewInt(71), acntDst.GetBalance()) - assert.Equal(t, 2, saveAccountCalled) + assert.Equal(t, 3, saveAccountCalled) } func TestTxProcessor_MoveBalanceWithFeesShouldWork(t *testing.T) { @@ -1072,7 +1072,7 @@ func TestTxProcessor_MoveBalanceWithFeesShouldWork(t *testing.T) { assert.Equal(t, uint64(5), acntSrc.GetNonce()) assert.Equal(t, big.NewInt(13), acntSrc.GetBalance()) assert.Equal(t, big.NewInt(71), acntDst.GetBalance()) - assert.Equal(t, 2, saveAccountCalled) + assert.Equal(t, 3, saveAccountCalled) } func TestTxProcessor_ProcessTransactionScDeployTxShouldWork(t *testing.T) { @@ -1324,7 +1324,7 @@ func TestTxProcessor_ProcessTransactionScTxShouldNotBeCalledWhenAdrDstIsNotInNod _, err := execTx.ProcessTransaction(&tx) assert.Nil(t, err) assert.False(t, wasCalled) - assert.Equal(t, 1, saveAccountCalled) + assert.Equal(t, 2, saveAccountCalled) } func TestTxProcessor_ProcessTxFeeIntraShard(t *testing.T) { @@ -2805,7 +2805,7 @@ func TestTxProcessor_ProcessRelayedTransactionV3(t *testing.T) { require.NotNil(t, txProcLocal) returnCode, err := txProcLocal.ProcessTransaction(&txCopy) - assert.Equal(t, process.ErrFailedTransaction, err) + assert.Equal(t, process.ErrNotEnoughGas, err) assert.Equal(t, vmcommon.UserError, returnCode) }) } From 28690762fa0c30620ddc481ff59fc2dcfb37800d Mon Sep 17 00:00:00 2001 From: Sorin Stanculeanu Date: Mon, 2 Dec 2024 18:39:18 +0200 Subject: [PATCH 22/39] further fixes after review, treat relayed v3 as normal tx --- factory/disabled/txCoordinator.go | 4 +- .../mock/transactionCoordinatorMock.go | 6 +- .../transactionAPI/apiTransactionProcessor.go | 6 +- .../apiTransactionProcessor_test.go | 8 +- node/node.go | 1 + process/block/preprocess/gasComputation.go | 4 +- .../block/preprocess/gasComputation_test.go | 44 +- process/block/preprocess/transactionsV2.go | 4 +- .../block/preprocess/transactionsV2_test.go | 6 +- process/coordinator/process.go | 2 +- process/coordinator/process_test.go | 8 +- process/coordinator/transactionType.go | 32 +- process/coordinator/transactionType_test.go | 52 ++- process/disabled/txTypeHandler.go | 4 +- .../factory/interceptedTxDataFactory.go | 1 + process/interface.go | 2 +- process/smartContract/process.go | 6 +- process/smartContract/process_test.go | 16 +- .../smartContract/processorV2/processV2.go | 217 +++++---- .../smartContract/processorV2/process_test.go | 16 +- .../smartContract/processorV2/vmInputV2.go | 14 +- process/transaction/baseProcess.go | 6 +- process/transaction/export_test.go | 6 + process/transaction/interceptedTransaction.go | 10 + .../interceptedTransaction_test.go | 58 +++ process/transaction/metaProcess.go | 8 +- process/transaction/metaProcess_test.go | 12 +- process/transaction/shardProcess.go | 421 ++---------------- process/transaction/shardProcess_test.go | 86 ++-- .../transactionEvaluator.go | 2 +- .../transactionEvaluator_test.go | 32 +- testscommon/scProcessorMock.go | 6 +- testscommon/transactionCoordinatorMock.go | 6 +- testscommon/txTypeHandlerMock.go | 6 +- 34 files changed, 441 insertions(+), 671 deletions(-) diff --git a/factory/disabled/txCoordinator.go b/factory/disabled/txCoordinator.go index 9d8002fb034..a5d0dfe330a 100644 --- a/factory/disabled/txCoordinator.go +++ b/factory/disabled/txCoordinator.go @@ -25,8 +25,8 @@ func (txCoordinator *TxCoordinator) CreateReceiptsHash() ([]byte, error) { } // ComputeTransactionType does nothing as it is disabled -func (txCoordinator *TxCoordinator) ComputeTransactionType(_ data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return 0, 0 +func (txCoordinator *TxCoordinator) ComputeTransactionType(_ data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return 0, 0, false } // RequestMiniBlocksAndTransactions does nothing as it is disabled diff --git a/integrationTests/mock/transactionCoordinatorMock.go b/integrationTests/mock/transactionCoordinatorMock.go index c002c52cc0f..29414c117da 100644 --- a/integrationTests/mock/transactionCoordinatorMock.go +++ b/integrationTests/mock/transactionCoordinatorMock.go @@ -12,7 +12,7 @@ import ( // TransactionCoordinatorMock - type TransactionCoordinatorMock struct { - ComputeTransactionTypeCalled func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) + ComputeTransactionTypeCalled func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) RequestMiniBlocksAndTransactionsCalled func(header data.HeaderHandler) RequestBlockTransactionsCalled func(body *block.Body) IsDataPreparedForProcessingCalled func(haveTime func() time.Duration) error @@ -55,9 +55,9 @@ func (tcm *TransactionCoordinatorMock) CreateReceiptsHash() ([]byte, error) { } // ComputeTransactionType - -func (tcm *TransactionCoordinatorMock) ComputeTransactionType(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { +func (tcm *TransactionCoordinatorMock) ComputeTransactionType(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { if tcm.ComputeTransactionTypeCalled == nil { - return process.MoveBalance, process.MoveBalance + return process.MoveBalance, process.MoveBalance, false } return tcm.ComputeTransactionTypeCalled(tx) diff --git a/node/external/transactionAPI/apiTransactionProcessor.go b/node/external/transactionAPI/apiTransactionProcessor.go index c6c568d46bf..37c4b56383c 100644 --- a/node/external/transactionAPI/apiTransactionProcessor.go +++ b/node/external/transactionAPI/apiTransactionProcessor.go @@ -183,7 +183,11 @@ func (atp *apiTransactionProcessor) PopulateComputedFields(tx *transaction.ApiTr } func (atp *apiTransactionProcessor) populateComputedFieldsProcessingType(tx *transaction.ApiTransactionResult) { - typeOnSource, typeOnDestination := atp.txTypeHandler.ComputeTransactionType(tx.Tx) + typeOnSource, typeOnDestination, isRelayedV3 := atp.txTypeHandler.ComputeTransactionType(tx.Tx) + if isRelayedV3 { + typeOnSource = process.RelayedTxV3 + typeOnDestination = process.RelayedTxV3 + } tx.ProcessingTypeOnSource = typeOnSource.String() tx.ProcessingTypeOnDestination = typeOnDestination.String() } diff --git a/node/external/transactionAPI/apiTransactionProcessor_test.go b/node/external/transactionAPI/apiTransactionProcessor_test.go index e6a7040fe87..52df3f50f82 100644 --- a/node/external/transactionAPI/apiTransactionProcessor_test.go +++ b/node/external/transactionAPI/apiTransactionProcessor_test.go @@ -1303,8 +1303,8 @@ func TestApiTransactionProcessor_GetTransactionPopulatesComputedFields(t *testin }) t.Run("ProcessingType", func(t *testing.T) { - txTypeHandler.ComputeTransactionTypeCalled = func(data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.MoveBalance, process.SCDeployment + txTypeHandler.ComputeTransactionTypeCalled = func(data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.MoveBalance, process.SCDeployment, false } dataPool.Transactions().AddData([]byte{0, 2}, &transaction.Transaction{Nonce: 7, SndAddr: []byte("alice"), RcvAddr: []byte("bob")}, 42, "1") @@ -1347,8 +1347,8 @@ func TestApiTransactionProcessor_PopulateComputedFields(t *testing.T) { require.Nil(t, err) require.NotNil(t, processor) - txTypeHandler.ComputeTransactionTypeCalled = func(data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.MoveBalance, process.SCDeployment + txTypeHandler.ComputeTransactionTypeCalled = func(data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.MoveBalance, process.SCDeployment, false } feeComputer.ComputeTransactionFeeCalled = func(tx *transaction.ApiTransactionResult) *big.Int { diff --git a/node/node.go b/node/node.go index 512d199a531..30fff1e6a78 100644 --- a/node/node.go +++ b/node/node.go @@ -798,6 +798,7 @@ func (n *Node) commonTransactionValidation( enableSignWithTxHash, n.coreComponents.TxSignHasher(), n.coreComponents.TxVersionChecker(), + n.coreComponents.EnableEpochsHandler(), ) if err != nil { return nil, nil, err diff --git a/process/block/preprocess/gasComputation.go b/process/block/preprocess/gasComputation.go index 628c6de455f..f4e6a82b4a9 100644 --- a/process/block/preprocess/gasComputation.go +++ b/process/block/preprocess/gasComputation.go @@ -374,7 +374,7 @@ func (gc *gasComputation) ComputeGasProvidedByTx( return txHandler.GetGasLimit(), txHandler.GetGasLimit(), nil } - txTypeSndShard, txTypeDstShard := gc.txTypeHandler.ComputeTransactionType(txHandler) + txTypeSndShard, txTypeDstShard, _ := gc.txTypeHandler.ComputeTransactionType(txHandler) isSCCall := txTypeDstShard == process.SCDeployment || txTypeDstShard == process.SCInvoking || txTypeDstShard == process.BuiltInFunctionCall @@ -403,7 +403,7 @@ func (gc *gasComputation) computeGasProvidedByTxV1( ) (uint64, uint64, error) { moveBalanceConsumption := gc.economicsFee.ComputeGasLimit(txHandler) - txTypeInShard, _ := gc.txTypeHandler.ComputeTransactionType(txHandler) + txTypeInShard, _, _ := gc.txTypeHandler.ComputeTransactionType(txHandler) isSCCall := txTypeInShard == process.SCDeployment || txTypeInShard == process.SCInvoking || txTypeInShard == process.BuiltInFunctionCall || diff --git a/process/block/preprocess/gasComputation_test.go b/process/block/preprocess/gasComputation_test.go index b59d8b45bf1..f60a7455fa6 100644 --- a/process/block/preprocess/gasComputation_test.go +++ b/process/block/preprocess/gasComputation_test.go @@ -214,8 +214,8 @@ func TestComputeGasProvidedByTx_ShouldWorkWhenTxReceiverAddressIsASmartContractI }, }, &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.SCInvoking, process.SCInvoking + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.SCInvoking, process.SCInvoking, false }}, createEnableEpochsHandler(), ) @@ -237,8 +237,8 @@ func TestComputeGasProvidedByTx_ShouldWorkWhenTxReceiverAddressIsASmartContractC }, }, &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.MoveBalance, process.SCInvoking + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.MoveBalance, process.SCInvoking, false }}, createEnableEpochsHandler(), ) @@ -260,8 +260,8 @@ func TestComputeGasProvidedByTx_ShouldReturnZeroIf0GasLimit(t *testing.T) { }, }, &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.MoveBalance, process.SCInvoking + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.MoveBalance, process.SCInvoking, false }}, createEnableEpochsHandler(), ) @@ -283,8 +283,8 @@ func TestComputeGasProvidedByTx_ShouldReturnGasLimitIfLessThanMoveBalance(t *tes }, }, &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.MoveBalance, process.SCInvoking + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.MoveBalance, process.SCInvoking, false }}, createEnableEpochsHandler(), ) @@ -306,8 +306,8 @@ func TestComputeGasProvidedByTx_ShouldReturnGasLimitWhenRelayed(t *testing.T) { }, }, &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.RelayedTx, process.RelayedTx + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.RelayedTx, process.RelayedTx, false }}, createEnableEpochsHandler(), ) @@ -329,8 +329,8 @@ func TestComputeGasProvidedByTx_ShouldReturnGasLimitWhenRelayedV2(t *testing.T) }, }, &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.RelayedTxV2, process.RelayedTxV2 + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.RelayedTxV2, process.RelayedTxV2, false }}, createEnableEpochsHandler(), ) @@ -413,11 +413,11 @@ func TestComputeGasProvidedByMiniBlock_ShouldWork(t *testing.T) { }, }, &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { if core.IsSmartContractAddress(tx.GetRcvAddr()) { - return process.MoveBalance, process.SCInvoking + return process.MoveBalance, process.SCInvoking, false } - return process.MoveBalance, process.MoveBalance + return process.MoveBalance, process.MoveBalance, false }}, createEnableEpochsHandler(), ) @@ -453,11 +453,11 @@ func TestComputeGasProvidedByMiniBlock_ShouldWorkV1(t *testing.T) { }, }, &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { if core.IsSmartContractAddress(tx.GetRcvAddr()) { - return process.SCInvoking, process.SCInvoking + return process.SCInvoking, process.SCInvoking, false } - return process.MoveBalance, process.MoveBalance + return process.MoveBalance, process.MoveBalance, false }}, enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) @@ -513,8 +513,8 @@ func TestComputeGasProvidedByTx_ShouldWorkWhenTxReceiverAddressIsASmartContractI }, }, &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.SCInvoking, process.SCInvoking + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.SCInvoking, process.SCInvoking, false }}, createEnableEpochsHandler(), ) @@ -536,8 +536,8 @@ func TestComputeGasProvidedByTx_ShouldWorkWhenTxReceiverAddressIsASmartContractC }, }, &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.SCInvoking, process.SCInvoking + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.SCInvoking, process.SCInvoking, false }}, enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) diff --git a/process/block/preprocess/transactionsV2.go b/process/block/preprocess/transactionsV2.go index 6391987983a..eec4dfe21bd 100644 --- a/process/block/preprocess/transactionsV2.go +++ b/process/block/preprocess/transactionsV2.go @@ -561,7 +561,7 @@ func (txs *transactions) getTxAndMbInfo( } numNewTxs := 1 - _, txTypeDstShard := txs.txTypeHandler.ComputeTransactionType(tx) + _, txTypeDstShard, _ := txs.txTypeHandler.ComputeTransactionType(tx) isReceiverSmartContractAddress := txTypeDstShard == process.SCDeployment || txTypeDstShard == process.SCInvoking isCrossShardScCallOrSpecialTx := receiverShardID != txs.shardCoordinator.SelfId() && (isReceiverSmartContractAddress || len(tx.RcvUserName) > 0) @@ -695,7 +695,7 @@ func (txs *transactions) shouldContinueProcessingScheduledTx( mbInfo.senderAddressToSkip = tx.GetSndAddr() - _, txTypeDstShard := txs.txTypeHandler.ComputeTransactionType(tx) + _, txTypeDstShard, _ := txs.txTypeHandler.ComputeTransactionType(tx) isReceiverSmartContractAddress := txTypeDstShard == process.SCDeployment || txTypeDstShard == process.SCInvoking if !isReceiverSmartContractAddress { return nil, nil, false diff --git a/process/block/preprocess/transactionsV2_test.go b/process/block/preprocess/transactionsV2_test.go index 9d4fb1cf686..1c86454ddda 100644 --- a/process/block/preprocess/transactionsV2_test.go +++ b/process/block/preprocess/transactionsV2_test.go @@ -66,11 +66,11 @@ func createTransactionPreprocessor() *transactions { }, EnableEpochsHandler: &enableEpochsHandlerMock.EnableEpochsHandlerStub{}, TxTypeHandler: &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { if bytes.Equal(tx.GetRcvAddr(), []byte("smart contract address")) { - return process.MoveBalance, process.SCInvoking + return process.MoveBalance, process.SCInvoking, false } - return process.MoveBalance, process.MoveBalance + return process.MoveBalance, process.MoveBalance, false }, }, ScheduledTxsExecutionHandler: &testscommon.ScheduledTxsExecutionStub{}, diff --git a/process/coordinator/process.go b/process/coordinator/process.go index 8a50d9f0b21..dbc68612649 100644 --- a/process/coordinator/process.go +++ b/process/coordinator/process.go @@ -1622,7 +1622,7 @@ func (tc *transactionCoordinator) checkGasProvidedByMiniBlockInReceiverShard( return process.ErrMissingTransaction } - _, txTypeDstShard := tc.txTypeHandler.ComputeTransactionType(txHandler) + _, txTypeDstShard, _ := tc.txTypeHandler.ComputeTransactionType(txHandler) moveBalanceGasLimit := tc.economicsFee.ComputeGasLimit(txHandler) if txTypeDstShard == process.MoveBalance { gasProvidedByTxInReceiverShard = moveBalanceGasLimit diff --git a/process/coordinator/process_test.go b/process/coordinator/process_test.go index d1dff667cb7..6b7fa57a9fd 100644 --- a/process/coordinator/process_test.go +++ b/process/coordinator/process_test.go @@ -3245,8 +3245,8 @@ func TestTransactionCoordinator_CheckGasProvidedByMiniBlockInReceiverShardShould }, }, TxTypeHandler: &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.MoveBalance, process.SCInvoking + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.MoveBalance, process.SCInvoking, false }, }, TransactionsLogProcessor: &mock.TxLogsProcessorStub{}, @@ -3302,8 +3302,8 @@ func TestTransactionCoordinator_CheckGasProvidedByMiniBlockInReceiverShardShould }, }, TxTypeHandler: &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.MoveBalance, process.SCInvoking + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.MoveBalance, process.SCInvoking, false }, }, TransactionsLogProcessor: &mock.TxLogsProcessorStub{}, diff --git a/process/coordinator/transactionType.go b/process/coordinator/transactionType.go index 77de8a5476a..871d670538c 100644 --- a/process/coordinator/transactionType.go +++ b/process/coordinator/transactionType.go @@ -77,63 +77,61 @@ func NewTxTypeHandler( } // ComputeTransactionType calculates the transaction type -func (tth *txTypeHandler) ComputeTransactionType(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { +func (tth *txTypeHandler) ComputeTransactionType(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { err := tth.checkTxValidity(tx) if err != nil { - return process.InvalidTransaction, process.InvalidTransaction + return process.InvalidTransaction, process.InvalidTransaction, false } - if common.IsValidRelayedTxV3(tx) { - return process.RelayedTxV3, process.RelayedTxV3 - } + isRelayedV3 := common.IsValidRelayedTxV3(tx) isEmptyAddress := tth.isDestAddressEmpty(tx) if isEmptyAddress { if len(tx.GetData()) > 0 { - return process.SCDeployment, process.SCDeployment + return process.SCDeployment, process.SCDeployment, isRelayedV3 } - return process.InvalidTransaction, process.InvalidTransaction + return process.InvalidTransaction, process.InvalidTransaction, isRelayedV3 } if len(tx.GetData()) == 0 { - return process.MoveBalance, process.MoveBalance + return process.MoveBalance, process.MoveBalance, isRelayedV3 } funcName, args := tth.getFunctionFromArguments(tx.GetData()) isBuiltInFunction := tth.isBuiltInFunctionCall(funcName) if isBuiltInFunction { if tth.isSCCallAfterBuiltIn(funcName, args, tx) { - return process.BuiltInFunctionCall, process.SCInvoking + return process.BuiltInFunctionCall, process.SCInvoking, isRelayedV3 } - return process.BuiltInFunctionCall, process.BuiltInFunctionCall + return process.BuiltInFunctionCall, process.BuiltInFunctionCall, isRelayedV3 } if isCallOfType(tx, vm.AsynchronousCallBack) { - return process.SCInvoking, process.SCInvoking + return process.SCInvoking, process.SCInvoking, isRelayedV3 } if len(funcName) == 0 { - return process.MoveBalance, process.MoveBalance + return process.MoveBalance, process.MoveBalance, isRelayedV3 } if tth.isRelayedTransactionV1(funcName) { - return process.RelayedTx, process.RelayedTx + return process.RelayedTx, process.RelayedTx, isRelayedV3 // this should never be reached with both relayed v1 and relayed v3 } if tth.isRelayedTransactionV2(funcName) { - return process.RelayedTxV2, process.RelayedTxV2 + return process.RelayedTxV2, process.RelayedTxV2, isRelayedV3 // this should never be reached with both relayed v2 and relayed v3 } isDestInSelfShard := tth.isDestAddressInSelfShard(tx.GetRcvAddr()) if isDestInSelfShard && core.IsSmartContractAddress(tx.GetRcvAddr()) { - return process.SCInvoking, process.SCInvoking + return process.SCInvoking, process.SCInvoking, isRelayedV3 } if core.IsSmartContractAddress(tx.GetRcvAddr()) { - return process.MoveBalance, process.SCInvoking + return process.MoveBalance, process.SCInvoking, isRelayedV3 } - return process.MoveBalance, process.MoveBalance + return process.MoveBalance, process.MoveBalance, isRelayedV3 } func isCallOfType(tx data.TransactionHandler, callType vm.CallType) bool { diff --git a/process/coordinator/transactionType_test.go b/process/coordinator/transactionType_test.go index 705b45c78e8..ef00e924141 100644 --- a/process/coordinator/transactionType_test.go +++ b/process/coordinator/transactionType_test.go @@ -124,9 +124,10 @@ func TestTxTypeHandler_ComputeTransactionTypeNil(t *testing.T) { assert.NotNil(t, tth) assert.Nil(t, err) - txTypeIn, txTypeCross := tth.ComputeTransactionType(nil) + txTypeIn, txTypeCross, isRelayedV3 := tth.ComputeTransactionType(nil) assert.Equal(t, process.InvalidTransaction, txTypeIn) assert.Equal(t, process.InvalidTransaction, txTypeCross) + assert.False(t, isRelayedV3) } func TestTxTypeHandler_ComputeTransactionTypeNilTx(t *testing.T) { @@ -145,9 +146,10 @@ func TestTxTypeHandler_ComputeTransactionTypeNilTx(t *testing.T) { tx.Value = big.NewInt(45) tx = nil - txTypeIn, txTypeCross := tth.ComputeTransactionType(tx) + txTypeIn, txTypeCross, isRelayedV3 := tth.ComputeTransactionType(tx) assert.Equal(t, process.InvalidTransaction, txTypeIn) assert.Equal(t, process.InvalidTransaction, txTypeCross) + assert.False(t, isRelayedV3) } func TestTxTypeHandler_ComputeTransactionTypeErrWrongTransaction(t *testing.T) { @@ -165,9 +167,10 @@ func TestTxTypeHandler_ComputeTransactionTypeErrWrongTransaction(t *testing.T) { tx.RcvAddr = nil tx.Value = big.NewInt(45) - txTypeIn, txTypeCross := tth.ComputeTransactionType(tx) + txTypeIn, txTypeCross, isRelayedV3 := tth.ComputeTransactionType(tx) assert.Equal(t, process.InvalidTransaction, txTypeIn) assert.Equal(t, process.InvalidTransaction, txTypeCross) + assert.False(t, isRelayedV3) } func TestTxTypeHandler_ComputeTransactionTypeScDeployment(t *testing.T) { @@ -186,9 +189,10 @@ func TestTxTypeHandler_ComputeTransactionTypeScDeployment(t *testing.T) { tx.Data = []byte("data") tx.Value = big.NewInt(45) - txTypeIn, txTypeCross := tth.ComputeTransactionType(tx) + txTypeIn, txTypeCross, isRelayedV3 := tth.ComputeTransactionType(tx) assert.Equal(t, process.SCDeployment, txTypeIn) assert.Equal(t, process.SCDeployment, txTypeCross) + assert.False(t, isRelayedV3) } func TestTxTypeHandler_ComputeTransactionTypeBuiltInFunctionCallNftTransfer(t *testing.T) { @@ -221,9 +225,10 @@ func TestTxTypeHandler_ComputeTransactionTypeBuiltInFunctionCallNftTransfer(t *t tx.Value = big.NewInt(45) - txTypeIn, txTypeCross := tth.ComputeTransactionType(tx) + txTypeIn, txTypeCross, isRelayedV3 := tth.ComputeTransactionType(tx) assert.Equal(t, process.BuiltInFunctionCall, txTypeIn) assert.Equal(t, process.SCInvoking, txTypeCross) + assert.False(t, isRelayedV3) } func TestTxTypeHandler_ComputeTransactionTypeBuiltInFunctionCallEsdtTransfer(t *testing.T) { @@ -250,9 +255,10 @@ func TestTxTypeHandler_ComputeTransactionTypeBuiltInFunctionCallEsdtTransfer(t * "@" + hex.EncodeToString(big.NewInt(10).Bytes())) tx.Value = big.NewInt(45) - txTypeIn, txTypeCross := tth.ComputeTransactionType(tx) + txTypeIn, txTypeCross, isRelayedV3 := tth.ComputeTransactionType(tx) assert.Equal(t, process.BuiltInFunctionCall, txTypeIn) assert.Equal(t, process.BuiltInFunctionCall, txTypeCross) + assert.False(t, isRelayedV3) } func TestTxTypeHandler_ComputeTransactionTypeRecv0AddressWrongTransaction(t *testing.T) { @@ -271,9 +277,10 @@ func TestTxTypeHandler_ComputeTransactionTypeRecv0AddressWrongTransaction(t *tes tx.Data = nil tx.Value = big.NewInt(45) - txTypeIn, txTypeCross := tth.ComputeTransactionType(tx) + txTypeIn, txTypeCross, isRelayedV3 := tth.ComputeTransactionType(tx) assert.Equal(t, process.InvalidTransaction, txTypeIn) assert.Equal(t, process.InvalidTransaction, txTypeCross) + assert.False(t, isRelayedV3) } func TestTxTypeHandler_ComputeTransactionTypeScInvoking(t *testing.T) { @@ -292,9 +299,10 @@ func TestTxTypeHandler_ComputeTransactionTypeScInvoking(t *testing.T) { assert.NotNil(t, tth) assert.Nil(t, err) - txTypeIn, txTypeCross := tth.ComputeTransactionType(tx) + txTypeIn, txTypeCross, isRelayedV3 := tth.ComputeTransactionType(tx) assert.Equal(t, process.SCInvoking, txTypeIn) assert.Equal(t, process.SCInvoking, txTypeCross) + assert.False(t, isRelayedV3) } func TestTxTypeHandler_ComputeTransactionTypeMoveBalance(t *testing.T) { @@ -318,9 +326,10 @@ func TestTxTypeHandler_ComputeTransactionTypeMoveBalance(t *testing.T) { assert.NotNil(t, tth) assert.Nil(t, err) - txTypeIn, txTypeCross := tth.ComputeTransactionType(tx) + txTypeIn, txTypeCross, isRelayedV3 := tth.ComputeTransactionType(tx) assert.Equal(t, process.MoveBalance, txTypeIn) assert.Equal(t, process.MoveBalance, txTypeCross) + assert.False(t, isRelayedV3) } func TestTxTypeHandler_ComputeTransactionTypeBuiltInFunc(t *testing.T) { @@ -347,9 +356,10 @@ func TestTxTypeHandler_ComputeTransactionTypeBuiltInFunc(t *testing.T) { assert.NotNil(t, tth) assert.Nil(t, err) - txTypeIn, txTypeCross := tth.ComputeTransactionType(tx) + txTypeIn, txTypeCross, isRelayedV3 := tth.ComputeTransactionType(tx) assert.Equal(t, process.BuiltInFunctionCall, txTypeIn) assert.Equal(t, process.BuiltInFunctionCall, txTypeCross) + assert.False(t, isRelayedV3) } func TestTxTypeHandler_ComputeTransactionTypeBuiltInFuncNotActiveMoveBalance(t *testing.T) { @@ -378,9 +388,10 @@ func TestTxTypeHandler_ComputeTransactionTypeBuiltInFuncNotActiveMoveBalance(t * assert.NotNil(t, tth) assert.Nil(t, err) - txTypeIn, txTypeCross := tth.ComputeTransactionType(tx) + txTypeIn, txTypeCross, isRelayedV3 := tth.ComputeTransactionType(tx) assert.Equal(t, process.MoveBalance, txTypeIn) assert.Equal(t, process.MoveBalance, txTypeCross) + assert.False(t, isRelayedV3) } func TestTxTypeHandler_ComputeTransactionTypeBuiltInFuncNotActiveSCCall(t *testing.T) { @@ -409,9 +420,10 @@ func TestTxTypeHandler_ComputeTransactionTypeBuiltInFuncNotActiveSCCall(t *testi assert.NotNil(t, tth) assert.Nil(t, err) - txTypeIn, txTypeCross := tth.ComputeTransactionType(tx) + txTypeIn, txTypeCross, isRelayedV3 := tth.ComputeTransactionType(tx) assert.Equal(t, process.SCInvoking, txTypeIn) assert.Equal(t, process.SCInvoking, txTypeCross) + assert.False(t, isRelayedV3) } func TestTxTypeHandler_ComputeTransactionTypeRelayedFunc(t *testing.T) { @@ -435,9 +447,10 @@ func TestTxTypeHandler_ComputeTransactionTypeRelayedFunc(t *testing.T) { assert.NotNil(t, tth) assert.Nil(t, err) - txTypeIn, txTypeCross := tth.ComputeTransactionType(tx) + txTypeIn, txTypeCross, isRelayedV3 := tth.ComputeTransactionType(tx) assert.Equal(t, process.RelayedTx, txTypeIn) assert.Equal(t, process.RelayedTx, txTypeCross) + assert.False(t, isRelayedV3) } func TestTxTypeHandler_ComputeTransactionTypeRelayedV2Func(t *testing.T) { @@ -461,9 +474,10 @@ func TestTxTypeHandler_ComputeTransactionTypeRelayedV2Func(t *testing.T) { assert.NotNil(t, tth) assert.Nil(t, err) - txTypeIn, txTypeCross := tth.ComputeTransactionType(tx) + txTypeIn, txTypeCross, isRelayedV3 := tth.ComputeTransactionType(tx) assert.Equal(t, process.RelayedTxV2, txTypeIn) assert.Equal(t, process.RelayedTxV2, txTypeCross) + assert.False(t, isRelayedV3) } func TestTxTypeHandler_ComputeTransactionTypeRelayedV3(t *testing.T) { @@ -489,9 +503,10 @@ func TestTxTypeHandler_ComputeTransactionTypeRelayedV3(t *testing.T) { assert.NotNil(t, tth) assert.Nil(t, err) - txTypeIn, txTypeCross := tth.ComputeTransactionType(tx) - assert.Equal(t, process.RelayedTxV3, txTypeIn) - assert.Equal(t, process.RelayedTxV3, txTypeCross) + txTypeIn, txTypeCross, isRelayedV3 := tth.ComputeTransactionType(tx) + assert.Equal(t, process.MoveBalance, txTypeIn) + assert.Equal(t, process.MoveBalance, txTypeCross) + assert.True(t, isRelayedV3) } func TestTxTypeHandler_ComputeTransactionTypeForSCRCallBack(t *testing.T) { @@ -516,7 +531,8 @@ func TestTxTypeHandler_ComputeTransactionTypeForSCRCallBack(t *testing.T) { assert.NotNil(t, tth) assert.Nil(t, err) - txTypeIn, txTypeCross := tth.ComputeTransactionType(tx) + txTypeIn, txTypeCross, isRelayedV3 := tth.ComputeTransactionType(tx) assert.Equal(t, process.SCInvoking, txTypeIn) assert.Equal(t, process.SCInvoking, txTypeCross) + assert.False(t, isRelayedV3) } diff --git a/process/disabled/txTypeHandler.go b/process/disabled/txTypeHandler.go index 302e81af555..dd405edff4d 100644 --- a/process/disabled/txTypeHandler.go +++ b/process/disabled/txTypeHandler.go @@ -17,9 +17,9 @@ func NewTxTypeHandler() *txTypeHandler { } // ComputeTransactionType always returns invalid transaction as it is disabled -func (handler *txTypeHandler) ComputeTransactionType(_ data.TransactionHandler) (process.TransactionType, process.TransactionType) { +func (handler *txTypeHandler) ComputeTransactionType(_ data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { log.Warn("disabled txTypeHandler ComputeTransactionType always returns invalid transaction") - return process.InvalidTransaction, process.InvalidTransaction + return process.InvalidTransaction, process.InvalidTransaction, false } // IsInterfaceNil returns true if there is no value under the interface diff --git a/process/interceptors/factory/interceptedTxDataFactory.go b/process/interceptors/factory/interceptedTxDataFactory.go index 563997c5066..0e1a568ad53 100644 --- a/process/interceptors/factory/interceptedTxDataFactory.go +++ b/process/interceptors/factory/interceptedTxDataFactory.go @@ -130,6 +130,7 @@ func (itdf *interceptedTxDataFactory) Create(buff []byte) (process.InterceptedDa itdf.enableEpochsHandler.IsFlagEnabled(common.TransactionSignedWithTxHashFlag), itdf.txSignHasher, itdf.txVersionChecker, + itdf.enableEpochsHandler, ) } diff --git a/process/interface.go b/process/interface.go index 747103f26ca..f7a4dfbd533 100644 --- a/process/interface.go +++ b/process/interface.go @@ -68,7 +68,7 @@ type SmartContractProcessorFacade interface { // TxTypeHandler is an interface to calculate the transaction type type TxTypeHandler interface { - ComputeTransactionType(tx data.TransactionHandler) (TransactionType, TransactionType) + ComputeTransactionType(tx data.TransactionHandler) (TransactionType, TransactionType, bool) IsInterfaceNil() bool } diff --git a/process/smartContract/process.go b/process/smartContract/process.go index 25031dcbf4a..8fbabd38df3 100644 --- a/process/smartContract/process.go +++ b/process/smartContract/process.go @@ -955,7 +955,7 @@ func (sc *scProcessor) doExecuteBuiltInFunction( return sc.finishSCExecution(make([]data.TransactionHandler, 0), txHash, tx, vmOutput, 0) } - _, txTypeOnDst := sc.txTypeHandler.ComputeTransactionType(tx) + _, txTypeOnDst, _ := sc.txTypeHandler.ComputeTransactionType(tx) builtInFuncGasUsed, err := sc.computeBuiltInFuncGasUsed(txTypeOnDst, vmInput.Function, vmInput.GasProvided, vmOutput.GasRemaining, check.IfNil(acntSnd)) log.LogIfError(err, "function", "ExecuteBuiltInFunction.computeBuiltInFuncGasUsed") @@ -1473,7 +1473,7 @@ func (sc *scProcessor) processIfErrorWithAddedLogs( Logs: processIfErrorLogs, }, 0) - txType, _ := sc.txTypeHandler.ComputeTransactionType(tx) + txType, _, _ := sc.txTypeHandler.ComputeTransactionType(tx) isCrossShardMoveBalance := txType == process.MoveBalance && check.IfNil(acntSnd) if isCrossShardMoveBalance && sc.enableEpochsHandler.IsFlagEnabled(common.SCDeployFlag) { // move balance was already consumed in sender shard @@ -2808,7 +2808,7 @@ func (sc *scProcessor) ProcessSmartContractResult(scr *smartContractResult.Smart gasLocked := sc.getGasLockedFromSCR(scr) - txType, _ := sc.txTypeHandler.ComputeTransactionType(scr) + txType, _, _ := sc.txTypeHandler.ComputeTransactionType(scr) switch txType { case process.MoveBalance: err = sc.processSimpleSCR(scr, txHash, dstAcc) diff --git a/process/smartContract/process_test.go b/process/smartContract/process_test.go index bc8caf169f3..b6c81113f45 100644 --- a/process/smartContract/process_test.go +++ b/process/smartContract/process_test.go @@ -3196,8 +3196,8 @@ func TestScProcessor_ProcessSmartContractResultDeploySCShouldError(t *testing.T) arguments.AccountsDB = accountsDB arguments.ShardCoordinator = shardCoordinator arguments.TxTypeHandler = &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.SCDeployment, process.SCDeployment + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.SCDeployment, process.SCDeployment, false }, } sc, err := NewSmartContractProcessor(arguments) @@ -3257,8 +3257,8 @@ func TestScProcessor_ProcessSmartContractResultExecuteSC(t *testing.T) { }, } arguments.TxTypeHandler = &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.SCInvoking, process.SCInvoking + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.SCInvoking, process.SCInvoking, false }, } sc, err := NewSmartContractProcessor(arguments) @@ -3320,8 +3320,8 @@ func TestScProcessor_ProcessSmartContractResultExecuteSCIfMetaAndBuiltIn(t *test }, } arguments.TxTypeHandler = &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.BuiltInFunctionCall, process.BuiltInFunctionCall + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.BuiltInFunctionCall, process.BuiltInFunctionCall, false }, } enableEpochsHandlerStub := enableEpochsHandlerMock.NewEnableEpochsHandlerStub(common.SCDeployFlag) @@ -3394,8 +3394,8 @@ func TestScProcessor_ProcessRelayedSCRValueBackToRelayer(t *testing.T) { }, } arguments.TxTypeHandler = &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.SCInvoking, process.SCInvoking + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.SCInvoking, process.SCInvoking, false }, } sc, err := NewSmartContractProcessor(arguments) diff --git a/process/smartContract/processorV2/processV2.go b/process/smartContract/processorV2/processV2.go index e34f0ac72aa..0dd370cb665 100644 --- a/process/smartContract/processorV2/processV2.go +++ b/process/smartContract/processorV2/processV2.go @@ -603,9 +603,6 @@ func (sc *scProcessor) updateDeveloperRewards( } moveBalanceGasLimit := sc.economicsFee.ComputeGasLimit(tx) - if common.IsValidRelayedTxV3(tx) { - moveBalanceGasLimit -= sc.economicsFee.MinGasLimit() // this was already consumed from the relayer - } if !isSmartContractResult(tx) { usedGasByMainSC, err = core.SafeSubUint64(usedGasByMainSC, moveBalanceGasLimit) if err != nil { @@ -747,9 +744,6 @@ func (sc *scProcessor) computeTotalConsumedFeeAndDevRwd( } moveBalanceGasLimit := sc.economicsFee.ComputeGasLimit(tx) - if common.IsValidRelayedTxV3(tx) { - moveBalanceGasLimit -= sc.economicsFee.MinGasLimit() // this was already consumed from the relayer - } if !isSmartContractResult(tx) { displayConsumedGas := consumedGas consumedGas, err = core.SafeSubUint64(consumedGas, moveBalanceGasLimit) @@ -811,15 +805,17 @@ func (sc *scProcessor) deleteSCRsWithValueZeroGoingToMeta(scrs []data.Transactio } func (sc *scProcessor) saveAccounts(acntSnd, acntDst vmcommon.AccountHandler) error { - if !check.IfNil(acntSnd) { - err := sc.accounts.SaveAccount(acntSnd) - if err != nil { - return err - } + err := sc.saveAccount(acntSnd) + if err != nil { + return err } - if !check.IfNil(acntDst) { - err := sc.accounts.SaveAccount(acntDst) + return sc.saveAccount(acntDst) +} + +func (sc *scProcessor) saveAccount(account vmcommon.AccountHandler) error { + if !check.IfNil(account) { + err := sc.accounts.SaveAccount(account) if err != nil { return err } @@ -937,7 +933,7 @@ func (sc *scProcessor) doExecuteBuiltInFunctionWithoutFailureProcessing( return sc.finishSCExecution(make([]data.TransactionHandler, 0), txHash, tx, vmOutput, 0) } - _, txTypeOnDst := sc.txTypeHandler.ComputeTransactionType(tx) + _, txTypeOnDst, _ := sc.txTypeHandler.ComputeTransactionType(tx) builtInFuncGasUsed, err := sc.computeBuiltInFuncGasUsed(txTypeOnDst, vmInput.Function, vmInput.GasProvided, vmOutput.GasRemaining, check.IfNil(acntSnd)) log.LogIfError(err, "function", "ExecuteBuiltInFunction.computeBuiltInFuncGasUsed") @@ -1521,7 +1517,7 @@ func (sc *scProcessor) processIfErrorWithAddedLogs(acntSnd state.UserAccountHand log.Debug("scProcessor.ProcessIfError() save log", "error", ignorableError.Error()) } - txType, _ := sc.txTypeHandler.ComputeTransactionType(tx) + txType, _, _ := sc.txTypeHandler.ComputeTransactionType(tx) isCrossShardMoveBalance := txType == process.MoveBalance && check.IfNil(acntSnd) if isCrossShardMoveBalance { // move balance was already consumed in sender shard @@ -1683,6 +1679,20 @@ func isRelayedSCR(tx data.TransactionHandler) (*smartContractResult.SmartContrac return nil, false } +func getRelayedValues(tx data.TransactionHandler) ([]byte, *big.Int) { + relayedTx, isRelayed := isRelayedSCR(tx) + if isRelayed { + return relayedTx.RelayerAddr, big.NewInt(0) + } + + if common.IsValidRelayedTxV3(tx) { + relayedTx := tx.(data.RelayedTransactionHandler) + return relayedTx.GetRelayerAddr(), big.NewInt(0) + } + + return nil, nil +} + // refunds the transaction values minus the relayed value to the sender account // in case of failed smart contract execution - gas is consumed, value is sent back func (sc *scProcessor) addBackTxValues( @@ -1927,19 +1937,23 @@ func (sc *scProcessor) processSCPayment(tx data.TransactionHandler, acntSnd stat return err } - cost := sc.economicsFee.ComputeTxFee(tx) - cost = cost.Add(cost, tx.GetValue()) + feePayer, err := sc.getFeePayer(tx, acntSnd) + if err != nil { + return err + } - if common.IsValidRelayedTxV3(tx) { - // for relayed v3, fee was consumed from relayer - cost = tx.GetValue() + fee := sc.economicsFee.ComputeTxFee(tx) + err = feePayer.SubFromBalance(fee) + if err != nil { + return err } - if cost.Cmp(big.NewInt(0)) == 0 { - return nil + err = sc.saveAccount(feePayer) + if err != nil { + return err } - err = acntSnd.SubFromBalance(cost) + err = acntSnd.SubFromBalance(tx.GetValue()) if err != nil { return err } @@ -1947,6 +1961,24 @@ func (sc *scProcessor) processSCPayment(tx data.TransactionHandler, acntSnd stat return nil } +func (sc *scProcessor) getFeePayer(tx data.TransactionHandler, acntSnd state.UserAccountHandler) (state.UserAccountHandler, error) { + if !common.IsValidRelayedTxV3(tx) { + return acntSnd, nil + } + + relayedTx, ok := tx.(data.RelayedTransactionHandler) + if !ok { + return acntSnd, nil + } + + account, err := sc.getAccountFromAddress(relayedTx.GetRelayerAddr()) + if err != nil { + return nil, err + } + + return account, nil +} + func (sc *scProcessor) processVMOutput( vmInput *vmcommon.VMInput, vmOutput *vmcommon.VMOutput, @@ -2268,17 +2300,7 @@ func createBaseSCR( result.CallType = vmData.DirectCall setOriginalTxHash(result, txHash, tx) - relayedTx, isRelayed := isRelayedSCR(tx) - if isRelayed { - result.RelayedValue = big.NewInt(0) - result.RelayerAddr = relayedTx.RelayerAddr - } - - if common.IsValidRelayedTxV3(tx) { - relayedTx := tx.(data.RelayedTransactionHandler) - result.RelayedValue = big.NewInt(0) - result.RelayerAddr = relayedTx.GetRelayerAddr() - } + result.RelayerAddr, result.RelayedValue = getRelayedValues(tx) return result } @@ -2316,17 +2338,8 @@ func (sc *scProcessor) createAsyncCallBackSCRFromVMOutput( OriginalSender: origScr.GetOriginalSender(), } setOriginalTxHash(scr, txHash, tx) - relayedTx, isRelayed := isRelayedSCR(tx) - if isRelayed { - scr.RelayedValue = big.NewInt(0) - scr.RelayerAddr = relayedTx.RelayerAddr - } - if common.IsValidRelayedTxV3(tx) { - relayedTx := tx.(data.RelayedTransactionHandler) - scr.RelayedValue = big.NewInt(0) - scr.RelayerAddr = relayedTx.GetRelayerAddr() - } + scr.RelayerAddr, scr.RelayedValue = getRelayedValues(tx) sc.addVMOutputResultsToSCR(vmOutput, scr) @@ -2591,54 +2604,7 @@ func (sc *scProcessor) createSCRForSenderAndRelayer( rcvAddress = tx.GetRcvAddr() } - var refundGasToRelayerSCR *smartContractResult.SmartContractResult - relayedSCR, isRelayed := isRelayedSCR(tx) - shouldRefundGasToRelayerSCR := isRelayed && callType != vmData.AsynchronousCall && gasRefund.Cmp(zero) > 0 - if shouldRefundGasToRelayerSCR { - senderForRelayerRefund := tx.GetRcvAddr() - if !sc.isSelfShard(tx.GetRcvAddr()) { - senderForRelayerRefund = tx.GetSndAddr() - } - - refundGasToRelayerSCR = &smartContractResult.SmartContractResult{ - Nonce: relayedSCR.Nonce + 1, - Value: big.NewInt(0).Set(gasRefund), - RcvAddr: relayedSCR.RelayerAddr, - SndAddr: senderForRelayerRefund, - PrevTxHash: txHash, - OriginalTxHash: relayedSCR.OriginalTxHash, - GasPrice: tx.GetGasPrice(), - CallType: vmData.DirectCall, - ReturnMessage: []byte("gas refund for relayer"), - OriginalSender: relayedSCR.OriginalSender, - } - gasRemaining = 0 - } - - isRelayedV3 := common.IsValidRelayedTxV3(tx) - shouldRefundGasToRelayerSCR = isRelayedV3 && callType != vmData.AsynchronousCall && gasRefund.Cmp(zero) > 0 - if shouldRefundGasToRelayerSCR { - senderForRelayerRefund := tx.GetRcvAddr() - if !sc.isSelfShard(tx.GetRcvAddr()) { - senderForRelayerRefund = tx.GetSndAddr() - } - - relayedTx := tx.(data.RelayedTransactionHandler) - - refundGasToRelayerSCR = &smartContractResult.SmartContractResult{ - Nonce: tx.GetNonce() + 1, - Value: big.NewInt(0).Set(gasRefund), - RcvAddr: relayedTx.GetRelayerAddr(), - SndAddr: senderForRelayerRefund, - PrevTxHash: txHash, - OriginalTxHash: txHash, - GasPrice: tx.GetGasPrice(), - CallType: vmData.DirectCall, - ReturnMessage: []byte("gas refund for relayer"), - OriginalSender: tx.GetSndAddr(), - } - gasRemaining = 0 - } + refundGasToRelayerSCR := sc.createRefundGasToRelayerSCRIfNeeded(tx, txHash, callType, gasRefund) scTx := &smartContractResult.SmartContractResult{} scTx.Value = big.NewInt(0).Set(storageFreeRefund) @@ -2669,6 +2635,71 @@ func (sc *scProcessor) createSCRForSenderAndRelayer( return scTx, refundGasToRelayerSCR } +func (sc *scProcessor) createRefundGasToRelayerSCRIfNeeded( + tx data.TransactionHandler, + txHash []byte, + callType vmData.CallType, + gasRefund *big.Int, +) *smartContractResult.SmartContractResult { + relayedSCR, isRelayed := isRelayedSCR(tx) + shouldRefundGasToRelayerSCR := isRelayed && callType != vmData.AsynchronousCall && gasRefund.Cmp(zero) > 0 + if shouldRefundGasToRelayerSCR { + return sc.createRefundGasToRelayerSCR( + tx, + relayedSCR.Nonce+1, + relayedSCR.RelayerAddr, + relayedSCR.OriginalSender, + relayedSCR.OriginalTxHash, + txHash, + gasRefund) + } + + isRelayedV3 := common.IsValidRelayedTxV3(tx) + shouldRefundGasToRelayerSCR = isRelayedV3 && callType != vmData.AsynchronousCall && gasRefund.Cmp(zero) > 0 + if shouldRefundGasToRelayerSCR { + relayedTx := tx.(data.RelayedTransactionHandler) + + return sc.createRefundGasToRelayerSCR( + tx, + tx.GetNonce()+1, + relayedTx.GetRelayerAddr(), + tx.GetSndAddr(), + txHash, + txHash, + gasRefund) + } + + return nil +} + +func (sc *scProcessor) createRefundGasToRelayerSCR( + tx data.TransactionHandler, + nonce uint64, + relayerAddr []byte, + originalSender []byte, + prevTxHash []byte, + originalTxHash []byte, + refund *big.Int, +) *smartContractResult.SmartContractResult { + senderForRelayerRefund := tx.GetRcvAddr() + if !sc.isSelfShard(tx.GetRcvAddr()) { + senderForRelayerRefund = tx.GetSndAddr() + } + + return &smartContractResult.SmartContractResult{ + Nonce: nonce, + Value: big.NewInt(0).Set(refund), + RcvAddr: relayerAddr, + SndAddr: senderForRelayerRefund, + PrevTxHash: prevTxHash, + OriginalTxHash: originalTxHash, + GasPrice: tx.GetGasPrice(), + CallType: vmData.DirectCall, + ReturnMessage: []byte("gas refund for relayer"), + OriginalSender: originalSender, + } +} + func addReturnDataToSCR(vmOutput *vmcommon.VMOutput, scTx *smartContractResult.SmartContractResult) { for _, retData := range vmOutput.ReturnData { scTx.Data = append(scTx.Data, []byte("@"+hex.EncodeToString(retData))...) @@ -2768,7 +2799,7 @@ func (sc *scProcessor) ProcessSmartContractResult(scr *smartContractResult.Smart gasLocked := sc.getGasLockedFromSCR(scr) - txType, _ := sc.txTypeHandler.ComputeTransactionType(scr) + txType, _, _ := sc.txTypeHandler.ComputeTransactionType(scr) switch txType { case process.MoveBalance: err = sc.processSimpleSCR(scr, txHash, dstAcc) diff --git a/process/smartContract/processorV2/process_test.go b/process/smartContract/processorV2/process_test.go index 905c18a033d..638e096005e 100644 --- a/process/smartContract/processorV2/process_test.go +++ b/process/smartContract/processorV2/process_test.go @@ -3129,8 +3129,8 @@ func TestScProcessor_ProcessSmartContractResultDeploySCShouldError(t *testing.T) arguments.AccountsDB = accountsDB arguments.ShardCoordinator = shardCoordinator arguments.TxTypeHandler = &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.SCDeployment, process.SCDeployment + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.SCDeployment, process.SCDeployment, false }, } sc, err := NewSmartContractProcessorV2(arguments) @@ -3190,8 +3190,8 @@ func TestScProcessor_ProcessSmartContractResultExecuteSC(t *testing.T) { }, } arguments.TxTypeHandler = &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.SCInvoking, process.SCInvoking + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.SCInvoking, process.SCInvoking, false }, } sc, err := NewSmartContractProcessorV2(arguments) @@ -3253,8 +3253,8 @@ func TestScProcessor_ProcessSmartContractResultExecuteSCIfMetaAndBuiltIn(t *test }, } arguments.TxTypeHandler = &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.BuiltInFunctionCall, process.BuiltInFunctionCall + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.BuiltInFunctionCall, process.BuiltInFunctionCall, false }, } enableEpochsHandlerStub := enableEpochsHandlerMock.NewEnableEpochsHandlerStub() @@ -3327,8 +3327,8 @@ func TestScProcessor_ProcessRelayedSCRValueBackToRelayer(t *testing.T) { }, } arguments.TxTypeHandler = &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.SCInvoking, process.SCInvoking + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.SCInvoking, process.SCInvoking, false }, } wasSaveLogsCalled := false diff --git a/process/smartContract/processorV2/vmInputV2.go b/process/smartContract/processorV2/vmInputV2.go index 44b20b1bcc3..81dd1f9360c 100644 --- a/process/smartContract/processorV2/vmInputV2.go +++ b/process/smartContract/processorV2/vmInputV2.go @@ -7,7 +7,6 @@ import ( "github.com/multiversx/mx-chain-core-go/data" "github.com/multiversx/mx-chain-core-go/data/smartContractResult" "github.com/multiversx/mx-chain-core-go/data/vm" - "github.com/multiversx/mx-chain-go/common" "github.com/multiversx/mx-chain-go/process" vmcommon "github.com/multiversx/mx-chain-vm-common-go" ) @@ -41,14 +40,7 @@ func (sc *scProcessor) initializeVMInputFromTx(vmInput *vmcommon.VMInput, tx dat vmInput.CallValue = new(big.Int).Set(tx.GetValue()) vmInput.GasPrice = tx.GetGasPrice() - relayedTx, isRelayed := isRelayedSCR(tx) - if isRelayed { - vmInput.RelayerAddr = relayedTx.RelayerAddr - } - if common.IsValidRelayedTxV3(tx) { - relayedTx := tx.(data.RelayedTransactionHandler) - vmInput.RelayerAddr = relayedTx.GetRelayerAddr() - } + vmInput.RelayerAddr, _ = getRelayedValues(tx) vmInput.GasProvided, err = sc.prepareGasProvided(tx) if err != nil { @@ -73,10 +65,6 @@ func (sc *scProcessor) prepareGasProvided(tx data.TransactionHandler) (uint64, e } gasForTxData := sc.economicsFee.ComputeGasLimit(tx) - if common.IsValidRelayedTxV3(tx) { - gasForTxData -= sc.economicsFee.MinGasLimit() // this was already consumed from the relayer - } - if tx.GetGasLimit() < gasForTxData { return 0, process.ErrNotEnoughGas } diff --git a/process/transaction/baseProcess.go b/process/transaction/baseProcess.go index 9aecdd534d4..5f745875547 100644 --- a/process/transaction/baseProcess.go +++ b/process/transaction/baseProcess.go @@ -120,7 +120,7 @@ func (txProc *baseTxProcessor) checkTxValues( isUserTxOfRelayed bool, ) error { if common.IsValidRelayedTxV3(tx) { - relayerAccount, _, err := txProc.getAccounts(tx.RelayerAddr, tx.RelayerAddr) + relayerAccount, err := txProc.getAccountFromAddress(tx.RelayerAddr) if err != nil { return err } @@ -244,7 +244,7 @@ func (txProc *baseTxProcessor) getFeePayer( return acntSnd, false, nil } - acntRelayer, _, err := txProc.getAccounts(tx.RelayerAddr, tx.RelayerAddr) + acntRelayer, err := txProc.getAccountFromAddress(tx.RelayerAddr) if err != nil { return nil, true, err } @@ -261,7 +261,7 @@ func (txProc *baseTxProcessor) computeInnerTxFee(tx *transaction.Transaction) *b } func (txProc *baseTxProcessor) computeInnerTxFeeAfterBaseCostFix(tx *transaction.Transaction) *big.Int { - _, dstShardTxType := txProc.txTypeHandler.ComputeTransactionType(tx) + _, dstShardTxType, _ := txProc.txTypeHandler.ComputeTransactionType(tx) if dstShardTxType == process.MoveBalance { return txProc.economicsFee.ComputeMoveBalanceFee(tx) } diff --git a/process/transaction/export_test.go b/process/transaction/export_test.go index f6c154dc6d1..9a3f4904cd0 100644 --- a/process/transaction/export_test.go +++ b/process/transaction/export_test.go @@ -6,6 +6,7 @@ import ( "github.com/multiversx/mx-chain-core-go/data" "github.com/multiversx/mx-chain-core-go/data/smartContractResult" "github.com/multiversx/mx-chain-core-go/data/transaction" + "github.com/multiversx/mx-chain-go/common" "github.com/multiversx/mx-chain-go/process" "github.com/multiversx/mx-chain-go/state" vmcommon "github.com/multiversx/mx-chain-vm-common-go" @@ -102,6 +103,11 @@ func (inTx *InterceptedTransaction) CheckMaxGasPrice() error { return inTx.checkMaxGasPrice() } +// SetEnableEpochsHandler sets the internal enable epochs handler +func (inTx *InterceptedTransaction) SetEnableEpochsHandler(handler common.EnableEpochsHandler) { + inTx.enableEpochsHandler = handler +} + // VerifyGuardian calls the un-exported method verifyGuardian func (txProc *txProcessor) VerifyGuardian(tx *transaction.Transaction, account state.UserAccountHandler) error { return txProc.verifyGuardian(tx, account) diff --git a/process/transaction/interceptedTransaction.go b/process/transaction/interceptedTransaction.go index b3c48a0eab2..533a0bf95c9 100644 --- a/process/transaction/interceptedTransaction.go +++ b/process/transaction/interceptedTransaction.go @@ -43,6 +43,7 @@ type InterceptedTransaction struct { sndShard uint32 isForCurrentShard bool enableSignedTxWithHash bool + enableEpochsHandler common.EnableEpochsHandler } // NewInterceptedTransaction returns a new instance of InterceptedTransaction @@ -62,6 +63,7 @@ func NewInterceptedTransaction( enableSignedTxWithHash bool, txSignHasher hashing.Hasher, txVersionChecker process.TxVersionCheckerHandler, + enableEpochsHandler common.EnableEpochsHandler, ) (*InterceptedTransaction, error) { if txBuff == nil { @@ -106,6 +108,9 @@ func NewInterceptedTransaction( if check.IfNil(txVersionChecker) { return nil, process.ErrNilTransactionVersionChecker } + if check.IfNil(enableEpochsHandler) { + return nil, process.ErrNilEnableEpochsHandler + } tx, err := createTx(protoMarshalizer, txBuff) if err != nil { @@ -128,6 +133,7 @@ func NewInterceptedTransaction( enableSignedTxWithHash: enableSignedTxWithHash, txVersionChecker: txVersionChecker, txSignHasher: txSignHasher, + enableEpochsHandler: enableEpochsHandler, } err = inTx.processFields(txBuff) @@ -238,6 +244,10 @@ func (inTx *InterceptedTransaction) verifyIfRelayedTxV3(tx *transaction.Transact return nil } + if !inTx.enableEpochsHandler.IsFlagEnabled(common.RelayedTransactionsV3Flag) { + return process.ErrRelayedTxV3Disabled + } + err := inTx.integrity(tx) if err != nil { return err diff --git a/process/transaction/interceptedTransaction_test.go b/process/transaction/interceptedTransaction_test.go index 5a95cc15a24..9e63b66d2e9 100644 --- a/process/transaction/interceptedTransaction_test.go +++ b/process/transaction/interceptedTransaction_test.go @@ -15,6 +15,7 @@ import ( "github.com/multiversx/mx-chain-core-go/data" dataTransaction "github.com/multiversx/mx-chain-core-go/data/transaction" "github.com/multiversx/mx-chain-crypto-go" + "github.com/multiversx/mx-chain-go/common" "github.com/multiversx/mx-chain-go/process" "github.com/multiversx/mx-chain-go/process/interceptors" "github.com/multiversx/mx-chain-go/process/mock" @@ -22,6 +23,7 @@ import ( "github.com/multiversx/mx-chain-go/process/transaction" "github.com/multiversx/mx-chain-go/testscommon" "github.com/multiversx/mx-chain-go/testscommon/economicsmocks" + "github.com/multiversx/mx-chain-go/testscommon/enableEpochsHandlerMock" "github.com/multiversx/mx-chain-go/testscommon/hashingMocks" "github.com/multiversx/mx-chain-go/testscommon/marshallerMock" logger "github.com/multiversx/mx-chain-logger-go" @@ -115,6 +117,7 @@ func createInterceptedTxWithTxFeeHandlerAndVersionChecker(tx *dataTransaction.Tr false, &hashingMocks.HasherMock{}, txVerChecker, + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) } @@ -158,6 +161,7 @@ func createInterceptedTxFromPlainTx(tx *dataTransaction.Transaction, txFeeHandle false, &hashingMocks.HasherMock{}, versioning.NewTxVersionChecker(minTxVersion), + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) } @@ -206,6 +210,7 @@ func createInterceptedTxFromPlainTxWithArgParser(tx *dataTransaction.Transaction false, &hashingMocks.HasherMock{}, versioning.NewTxVersionChecker(tx.Version), + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(common.RelayedTransactionsV3Flag), ) } @@ -230,6 +235,7 @@ func TestNewInterceptedTransaction_NilBufferShouldErr(t *testing.T) { false, &hashingMocks.HasherMock{}, versioning.NewTxVersionChecker(1), + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) assert.Nil(t, txi) @@ -255,6 +261,7 @@ func TestNewInterceptedTransaction_NilArgsParser(t *testing.T) { false, &hashingMocks.HasherMock{}, versioning.NewTxVersionChecker(1), + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) assert.Nil(t, txi) @@ -280,6 +287,7 @@ func TestNewInterceptedTransaction_NilVersionChecker(t *testing.T) { false, &hashingMocks.HasherMock{}, nil, + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) assert.Nil(t, txi) @@ -305,6 +313,7 @@ func TestNewInterceptedTransaction_NilMarshalizerShouldErr(t *testing.T) { false, &hashingMocks.HasherMock{}, versioning.NewTxVersionChecker(1), + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) assert.Nil(t, txi) @@ -330,6 +339,7 @@ func TestNewInterceptedTransaction_NilSignMarshalizerShouldErr(t *testing.T) { false, &hashingMocks.HasherMock{}, versioning.NewTxVersionChecker(1), + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) assert.Nil(t, txi) @@ -355,6 +365,7 @@ func TestNewInterceptedTransaction_NilHasherShouldErr(t *testing.T) { false, &hashingMocks.HasherMock{}, versioning.NewTxVersionChecker(1), + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) assert.Nil(t, txi) @@ -380,6 +391,7 @@ func TestNewInterceptedTransaction_NilKeyGenShouldErr(t *testing.T) { false, &hashingMocks.HasherMock{}, versioning.NewTxVersionChecker(1), + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) assert.Nil(t, txi) @@ -405,6 +417,7 @@ func TestNewInterceptedTransaction_NilSignerShouldErr(t *testing.T) { false, &hashingMocks.HasherMock{}, versioning.NewTxVersionChecker(1), + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) assert.Nil(t, txi) @@ -430,6 +443,7 @@ func TestNewInterceptedTransaction_NilPubkeyConverterShouldErr(t *testing.T) { false, &hashingMocks.HasherMock{}, versioning.NewTxVersionChecker(1), + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) assert.Nil(t, txi) @@ -455,6 +469,7 @@ func TestNewInterceptedTransaction_NilCoordinatorShouldErr(t *testing.T) { false, &hashingMocks.HasherMock{}, versioning.NewTxVersionChecker(1), + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) assert.Nil(t, txi) @@ -480,6 +495,7 @@ func TestNewInterceptedTransaction_NilFeeHandlerShouldErr(t *testing.T) { false, &hashingMocks.HasherMock{}, versioning.NewTxVersionChecker(1), + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) assert.Nil(t, txi) @@ -505,6 +521,7 @@ func TestNewInterceptedTransaction_NilWhiteListerVerifiedTxsShouldErr(t *testing false, &hashingMocks.HasherMock{}, versioning.NewTxVersionChecker(1), + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) assert.Nil(t, txi) @@ -530,6 +547,7 @@ func TestNewInterceptedTransaction_InvalidChainIDShouldErr(t *testing.T) { false, &hashingMocks.HasherMock{}, versioning.NewTxVersionChecker(1), + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) assert.Nil(t, txi) @@ -555,12 +573,39 @@ func TestNewInterceptedTransaction_NilTxSignHasherShouldErr(t *testing.T) { false, nil, versioning.NewTxVersionChecker(1), + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) assert.Nil(t, txi) assert.Equal(t, process.ErrNilHasher, err) } +func TestNewInterceptedTransaction_NilEnableEpochsHandlerShouldErr(t *testing.T) { + t.Parallel() + + txi, err := transaction.NewInterceptedTransaction( + make([]byte, 0), + &mock.MarshalizerMock{}, + &mock.MarshalizerMock{}, + &hashingMocks.HasherMock{}, + &mock.SingleSignKeyGenMock{}, + &mock.SignerMock{}, + createMockPubKeyConverter(), + mock.NewOneShardCoordinatorMock(), + &economicsmocks.EconomicsHandlerStub{}, + &testscommon.WhiteListHandlerStub{}, + &testscommon.ArgumentParserMock{}, + []byte("chainID"), + false, + &hashingMocks.HasherMock{}, + versioning.NewTxVersionChecker(1), + nil, + ) + + assert.Nil(t, txi) + assert.Equal(t, process.ErrNilEnableEpochsHandler, err) +} + func TestNewInterceptedTransaction_UnmarshalingTxFailsShouldErr(t *testing.T) { t.Parallel() @@ -586,6 +631,7 @@ func TestNewInterceptedTransaction_UnmarshalingTxFailsShouldErr(t *testing.T) { false, &hashingMocks.HasherMock{}, versioning.NewTxVersionChecker(1), + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) assert.Nil(t, txi) @@ -1056,6 +1102,7 @@ func TestInterceptedTransaction_CheckValiditySignedWithHashButNotEnabled(t *test false, &hashingMocks.HasherMock{}, versioning.NewTxVersionChecker(minTxVersion), + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) err := txi.CheckValidity() @@ -1116,6 +1163,7 @@ func TestInterceptedTransaction_CheckValiditySignedWithHashShouldWork(t *testing true, &hashingMocks.HasherMock{}, versioning.NewTxVersionChecker(minTxVersion), + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) err := txi.CheckValidity() @@ -1201,6 +1249,7 @@ func TestInterceptedTransaction_ScTxDeployRecvShardIdShouldBeSendersShardId(t *t false, &hashingMocks.HasherMock{}, versioning.NewTxVersionChecker(minTxVersion), + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) assert.Nil(t, err) @@ -1340,6 +1389,7 @@ func TestInterceptedTransaction_CheckValiditySecondTimeDoesNotVerifySig(t *testi false, &hashingMocks.HasherMock{}, versioning.NewTxVersionChecker(minTxVersion), + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) require.Nil(t, err) @@ -1529,6 +1579,12 @@ func TestInterceptedTransaction_CheckValidityOfRelayedTxV3(t *testing.T) { tx.Signature = sigOk tx.RelayerSignature = sigOk + // flag not active should error + txi, _ = createInterceptedTxFromPlainTxWithArgParser(tx) + txi.SetEnableEpochsHandler(enableEpochsHandlerMock.NewEnableEpochsHandlerStub()) + err = txi.CheckValidity() + assert.Equal(t, process.ErrRelayedTxV3Disabled, err) + // sender in different shard than relayer should fail tx.RelayerAddr = bytes.Repeat([]byte("a"), len(relayerAddress)) txi, _ = createInterceptedTxFromPlainTxWithArgParser(tx) @@ -1698,6 +1754,7 @@ func TestInterceptedTransaction_Fee(t *testing.T) { false, &hashingMocks.HasherMock{}, versioning.NewTxVersionChecker(0), + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) assert.Equal(t, big.NewInt(0), txin.Fee()) @@ -1741,6 +1798,7 @@ func TestInterceptedTransaction_String(t *testing.T) { false, &hashingMocks.HasherMock{}, versioning.NewTxVersionChecker(0), + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) expectedFormat := fmt.Sprintf( diff --git a/process/transaction/metaProcess.go b/process/transaction/metaProcess.go index 577b9c963fb..090d4ad89e4 100644 --- a/process/transaction/metaProcess.go +++ b/process/transaction/metaProcess.go @@ -135,13 +135,7 @@ func (txProc *metaTxProcessor) ProcessTransaction(tx *transaction.Transaction) ( return 0, err } - txCopy := *tx - txType, _ := txProc.txTypeHandler.ComputeTransactionType(tx) - if txType == process.RelayedTxV3 { - // extract the inner transaction in order to get the proper user tx type - txCopy.RelayerSignature = nil - txType, _ = txProc.txTypeHandler.ComputeTransactionType(&txCopy) - } + txType, _, _ := txProc.txTypeHandler.ComputeTransactionType(tx) switch txType { case process.SCDeployment: return txProc.processSCDeployment(tx, tx.SndAddr) diff --git a/process/transaction/metaProcess_test.go b/process/transaction/metaProcess_test.go index eaaa1382d2e..42a04260077 100644 --- a/process/transaction/metaProcess_test.go +++ b/process/transaction/metaProcess_test.go @@ -277,8 +277,8 @@ func TestMetaTxProcessor_ProcessTransactionScTxShouldWork(t *testing.T) { args.Accounts = adb args.ScProcessor = scProcessorMock args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.SCInvoking, process.SCInvoking + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.SCInvoking, process.SCInvoking, false }, } txProc, _ := txproc.NewMetaTxProcessor(args) @@ -323,8 +323,8 @@ func TestMetaTxProcessor_ProcessTransactionScTxShouldReturnErrWhenExecutionFails args.Accounts = adb args.ScProcessor = scProcessorMock args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.SCInvoking, process.SCInvoking + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.SCInvoking, process.SCInvoking, false }, } txProc, _ := txproc.NewMetaTxProcessor(args) @@ -439,8 +439,8 @@ func TestMetaTxProcessor_ProcessTransactionBuiltInCallTxShouldWork(t *testing.T) args.Accounts = adb args.ScProcessor = scProcessorMock args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.BuiltInFunctionCall, process.BuiltInFunctionCall + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.BuiltInFunctionCall, process.BuiltInFunctionCall, false }, } enableEpochsHandlerStub := enableEpochsHandlerMock.NewEnableEpochsHandlerStub(common.ESDTFlag) diff --git a/process/transaction/shardProcess.go b/process/transaction/shardProcess.go index 5091fe71ba6..354ff22ef08 100644 --- a/process/transaction/shardProcess.go +++ b/process/transaction/shardProcess.go @@ -196,7 +196,7 @@ func (txProc *txProcessor) ProcessTransaction(tx *transaction.Transaction) (vmco txProc.pubkeyConv, ) - txType, dstShardTxType := txProc.txTypeHandler.ComputeTransactionType(tx) + txType, dstShardTxType, isRelayedV3 := txProc.txTypeHandler.ComputeTransactionType(tx) err = txProc.checkTxValues(tx, acntSnd, acntDst, false) if err != nil { if errors.Is(err, process.ErrInsufficientFunds) { @@ -223,9 +223,16 @@ func (txProc *txProcessor) ProcessTransaction(tx *transaction.Transaction) (vmco return vmcommon.UserError, err } + if isRelayedV3 { + err = txProc.verifyRelayedTxV3(tx) + if err != nil { + return vmcommon.UserError, err + } + } + switch txType { case process.MoveBalance: - err = txProc.processMoveBalance(tx, acntSnd, acntDst, dstShardTxType, nil, false, false) + err = txProc.processMoveBalance(tx, acntSnd, acntDst, dstShardTxType, nil, false) if err != nil { return vmcommon.UserError, txProc.executeAfterFailedMoveBalanceTransaction(tx, err) } @@ -240,8 +247,6 @@ func (txProc *txProcessor) ProcessTransaction(tx *transaction.Transaction) (vmco return txProc.processRelayedTx(tx, acntSnd, acntDst) case process.RelayedTxV2: return txProc.processRelayedTxV2(tx, acntSnd, acntDst) - case process.RelayedTxV3: - return txProc.processRelayedTxV3(tx) } return vmcommon.UserError, txProc.executingFailedTransaction(tx, acntSnd, process.ErrWrongTransaction) @@ -351,75 +356,16 @@ func (txProc *txProcessor) executingFailedTransaction( return process.ErrFailedTransaction } -func (txProc *txProcessor) executingFailedTransactionRelayedV3( - tx *transaction.Transaction, - acntSnd state.UserAccountHandler, - relayerAccount state.UserAccountHandler, - txError error, -) error { - if check.IfNil(relayerAccount) { - return nil - } - if check.IfNil(acntSnd) { - return nil - } - - txFee := txProc.economicsFee.ComputeTxFee(tx) - err := relayerAccount.SubFromBalance(txFee) - if err != nil { - return err - } - - txHash, err := core.CalculateHash(txProc.marshalizer, txProc.hasher, tx) - if err != nil { - return err - } - - acntSnd.IncreaseNonce(1) - err = txProc.badTxForwarder.AddIntermediateTransactions([]data.TransactionHandler{tx}, txHash) - if err != nil { - return err - } - - log.Trace("executingFailedTransactionRelayedV3", "fail reason(error)", txError, "tx hash", txHash) - - rpt := &receipt.Receipt{ - Value: big.NewInt(0).Set(txFee), - SndAddr: relayerAccount.AddressBytes(), - Data: []byte(txError.Error()), - TxHash: txHash, - } - - err = txProc.receiptForwarder.AddIntermediateTransactions([]data.TransactionHandler{rpt}, txHash) - if err != nil { - return err - } - - txProc.txFeeHandler.ProcessTransactionFee(txFee, big.NewInt(0), txHash) - - err = txProc.accounts.SaveAccount(relayerAccount) - if err != nil { - return err - } - - err = txProc.accounts.SaveAccount(acntSnd) - if err != nil { - return err - } - - return process.ErrFailedTransaction -} - func (txProc *txProcessor) createReceiptWithReturnedGas( txHash []byte, tx *transaction.Transaction, - acntSnd state.UserAccountHandler, + feePayer state.UserAccountHandler, moveBalanceCost *big.Int, totalProvided *big.Int, destShardTxType process.TransactionType, isUserTxOfRelayed bool, ) error { - if check.IfNil(acntSnd) || isUserTxOfRelayed { + if check.IfNil(feePayer) || isUserTxOfRelayed { return nil } shouldCreateReceiptBackwardCompatible := !txProc.enableEpochsHandler.IsFlagEnabled(common.MetaProtectionFlag) && core.IsSmartContractAddress(tx.RcvAddr) @@ -436,38 +382,33 @@ func (txProc *txProcessor) createReceiptWithReturnedGas( rpt := &receipt.Receipt{ Value: big.NewInt(0).Set(refundValue), - SndAddr: tx.SndAddr, + SndAddr: feePayer.AddressBytes(), Data: []byte(RefundGasMessage), TxHash: txHash, } - err := txProc.receiptForwarder.AddIntermediateTransactions([]data.TransactionHandler{rpt}, txHash) - if err != nil { - return err - } - - return nil + return txProc.receiptForwarder.AddIntermediateTransactions([]data.TransactionHandler{rpt}, txHash) } func (txProc *txProcessor) processTxFee( tx *transaction.Transaction, - acntSnd, acntDst state.UserAccountHandler, + feePayer, acntDst state.UserAccountHandler, dstShardTxType process.TransactionType, isUserTxOfRelayed bool, ) (*big.Int, *big.Int, error) { - if check.IfNil(acntSnd) { + if check.IfNil(feePayer) { return big.NewInt(0), big.NewInt(0), nil } if isUserTxOfRelayed { totalCost := txProc.computeInnerTxFee(tx) - err := acntSnd.SubFromBalance(totalCost) + err := feePayer.SubFromBalance(totalCost) if err != nil { return nil, nil, err } - err = txProc.accounts.SaveAccount(acntSnd) + err = txProc.accounts.SaveAccount(feePayer) if err != nil { return nil, nil, err } @@ -496,76 +437,23 @@ func (txProc *txProcessor) processTxFee( if dstShardTxType != process.MoveBalance || (!txProc.enableEpochsHandler.IsFlagEnabled(common.MetaProtectionFlag) && isCrossShardSCCall) { - err := acntSnd.SubFromBalance(totalCost) - if err != nil { - return nil, nil, err - } - - err = txProc.accounts.SaveAccount(acntSnd) + err := feePayer.SubFromBalance(totalCost) if err != nil { return nil, nil, err } } else { - err := acntSnd.SubFromBalance(moveBalanceFee) + err := feePayer.SubFromBalance(moveBalanceFee) if err != nil { return nil, nil, err } - - err = txProc.accounts.SaveAccount(acntSnd) - if err != nil { - return nil, nil, err - } - } - - return moveBalanceFee, totalCost, nil -} - -func (txProc *txProcessor) processTxFeeOfRelayedV3( - tx *transaction.Transaction, - txHash []byte, - relayerAccount state.UserAccountHandler, - destinationAccount state.UserAccountHandler, - dstShardTxType process.TransactionType, -) error { - if check.IfNil(relayerAccount) { - return nil - } - - moveBalanceFee := txProc.economicsFee.ComputeMoveBalanceFee(tx) - totalCost := txProc.economicsFee.ComputeTxFee(tx) - - if !txProc.enableEpochsHandler.IsFlagEnabled(common.PenalizedTooMuchGasFlag) { - totalCost = core.SafeMul(tx.GasLimit, tx.GasPrice) } - isCrossShardSCCall := check.IfNil(destinationAccount) && len(tx.GetData()) > 0 && core.IsSmartContractAddress(tx.GetRcvAddr()) - if dstShardTxType != process.MoveBalance || - (!txProc.enableEpochsHandler.IsFlagEnabled(common.MetaProtectionFlag) && isCrossShardSCCall) { - - err := relayerAccount.SubFromBalance(totalCost) - if err != nil { - return err - } - - err = txProc.accounts.SaveAccount(relayerAccount) - if err != nil { - return err - } - } else { - err := relayerAccount.SubFromBalance(moveBalanceFee) - if err != nil { - return err - } - - txProc.txFeeHandler.ProcessTransactionFee(moveBalanceFee, big.NewInt(0), txHash) - - err = txProc.accounts.SaveAccount(relayerAccount) - if err != nil { - return err - } + err := txProc.accounts.SaveAccount(feePayer) + if err != nil { + return nil, nil, err } - return nil + return moveBalanceFee, totalCost, nil } func (txProc *txProcessor) checkIfValidTxToMetaChain(tx *transaction.Transaction) error { @@ -596,10 +484,13 @@ func (txProc *txProcessor) processMoveBalance( destShardTxType process.TransactionType, originalTxHash []byte, isUserTxOfRelayed bool, - isUserTxOfRelayedV3 bool, ) error { - moveBalanceCost, totalCost, err := txProc.processTxFee(tx, acntSrc, acntDst, destShardTxType, isUserTxOfRelayed) + feePayer, _, err := txProc.getFeePayer(tx, acntSrc) + if err != nil { + return nil + } + moveBalanceCost, totalCost, err := txProc.processTxFee(tx, feePayer, acntDst, destShardTxType, isUserTxOfRelayed) if err != nil { return err } @@ -659,14 +550,7 @@ func (txProc *txProcessor) processMoveBalance( } txHash := originalTxHash - if !isUserTxOfRelayedV3 { - txHash, err = core.CalculateHash(txProc.marshalizer, txProc.hasher, tx) - if err != nil { - return err - } - } - - err = txProc.createReceiptWithReturnedGas(txHash, tx, acntSrc, moveBalanceCost, totalCost, destShardTxType, isUserTxOfRelayed) + err = txProc.createReceiptWithReturnedGas(txHash, tx, feePayer, moveBalanceCost, totalCost, destShardTxType, isUserTxOfRelayed) if err != nil { return err } @@ -680,76 +564,6 @@ func (txProc *txProcessor) processMoveBalance( return nil } -func (txProc *txProcessor) processMoveBalanceOfRelayedV3( - tx *transaction.Transaction, - senderAccount state.UserAccountHandler, - destinationAccount state.UserAccountHandler, - relayerAccount state.UserAccountHandler, - destShardTxType process.TransactionType, - originalTxHash []byte, -) error { - err := txProc.processTxFeeOfRelayedV3(tx, originalTxHash, relayerAccount, destinationAccount, destShardTxType) - if err != nil { - return err - } - - // is sender address in node shard - if !check.IfNil(senderAccount) { - senderAccount.IncreaseNonce(1) - err = senderAccount.SubFromBalance(tx.Value) - if err != nil { - return err - } - - err = txProc.accounts.SaveAccount(senderAccount) - if err != nil { - return err - } - } - - isPayable, err := txProc.scProcessor.IsPayable(tx.SndAddr, tx.RcvAddr) - if err != nil { - errRefund := txProc.revertConsumedValueFromSender(tx, senderAccount, true) - if errRefund != nil { - log.Error("failed to return funds to sender after check if receiver is payable", "error", errRefund) - } - return err - } - if !isPayable { - err = txProc.revertConsumedValueFromSender(tx, senderAccount, true) - if err != nil { - log.Error("failed to return funds to sender while transferring to non payable sc", "error", err) - } - - return process.ErrAccountNotPayable - } - - err = txProc.checkIfValidTxToMetaChain(tx) - if err != nil { - errLocal := txProc.revertConsumedValueFromSender(tx, senderAccount, true) - if errLocal != nil { - log.Error("failed to return funds to sender while sending invalid tx to metachain", "error", errLocal) - } - - return err - } - - // is receiver address in node shard - if !check.IfNil(destinationAccount) { - err = destinationAccount.AddToBalance(tx.Value) - if err != nil { - return err - } - - err = txProc.accounts.SaveAccount(destinationAccount) - if err != nil { - return err - } - } - - return nil -} - func (txProc *txProcessor) revertConsumedValueFromSender( tx *transaction.Transaction, acntSrc state.UserAccountHandler, @@ -806,88 +620,6 @@ func makeUserTxFromRelayedTxV2Args(args [][]byte) *transaction.Transaction { return userTx } -func (txProc *txProcessor) finishExecutionOfRelayedTxV3( - relayerAccount state.UserAccountHandler, - tx *transaction.Transaction, - userTx *transaction.Transaction, -) (vmcommon.ReturnCode, error) { - txHash, err := core.CalculateHash(txProc.marshalizer, txProc.hasher, tx) - if err != nil { - return 0, err - } - - senderAccount, destinationAccount, err := txProc.getAccounts(tx.SndAddr, tx.RcvAddr) - if err != nil { - errRemove := txProc.increaseSenderNonceIfNeededAndLog(txHash, tx, senderAccount, err) - if errRemove != nil { - return vmcommon.UserError, errRemove - } - return vmcommon.UserError, txProc.executeFailedRelayedV3UserTx( - tx, - txHash, - err.Error()) - } - - txType, dstShardTxType := txProc.txTypeHandler.ComputeTransactionType(userTx) - returnCode := vmcommon.Ok - switch txType { - case process.MoveBalance: - err = txProc.processMoveBalanceOfRelayedV3(tx, senderAccount, destinationAccount, relayerAccount, dstShardTxType, txHash) - case process.SCDeployment: - err = txProc.processTxFeeOfRelayedV3(tx, txHash, relayerAccount, destinationAccount, dstShardTxType) - if err != nil { - break - } - - returnCode, err = txProc.scProcessor.DeploySmartContract(tx, destinationAccount) - case process.SCInvoking: - err = txProc.processTxFeeOfRelayedV3(tx, txHash, relayerAccount, destinationAccount, dstShardTxType) - if err != nil { - break - } - - returnCode, err = txProc.scProcessor.ExecuteSmartContractTransaction(tx, senderAccount, destinationAccount) - case process.BuiltInFunctionCall: - err = txProc.processTxFeeOfRelayedV3(tx, txHash, relayerAccount, destinationAccount, dstShardTxType) - if err != nil { - break - } - - returnCode, err = txProc.scProcessor.ExecuteBuiltInFunction(tx, senderAccount, destinationAccount) - default: - err = process.ErrWrongTransaction - errRemove := txProc.increaseSenderNonceIfNeededAndLog(txHash, tx, senderAccount, err) - if errRemove != nil { - return vmcommon.UserError, errRemove - } - return vmcommon.UserError, txProc.executeFailedRelayedV3UserTx( - tx, - txHash, - err.Error()) - } - - if errors.Is(err, process.ErrInvalidMetaTransaction) || errors.Is(err, process.ErrAccountNotPayable) { - return vmcommon.UserError, txProc.executeFailedRelayedV3UserTx( - tx, - txHash, - err.Error()) - } - - if errors.Is(err, process.ErrFailedTransaction) { - // in case of failed inner user tx transaction we should just simply return execution failed and - // not failed transaction - as the actual transaction (the relayed we correctly executed) and thus - // it should not lend in the invalid miniblock - return vmcommon.ExecutionFailed, nil - } - - if err != nil { - log.Error("processUserTx", "protocolError", err) - return vmcommon.ExecutionFailed, err - } - - return returnCode, err -} - func (txProc *txProcessor) finishExecutionOfRelayedTx( relayerAcnt, acntDst state.UserAccountHandler, tx *transaction.Transaction, @@ -969,39 +701,29 @@ func (txProc *txProcessor) addFeeAndValueToDest(acntDst state.UserAccountHandler return txProc.accounts.SaveAccount(acntDst) } -func (txProc *txProcessor) processRelayedTxV3(tx *transaction.Transaction) (vmcommon.ReturnCode, error) { - relayerAccount, sndAccount, err := txProc.getAccounts(tx.RelayerAddr, tx.SndAddr) - if err != nil { - return 0, err - } - +func (txProc *txProcessor) verifyRelayedTxV3(tx *transaction.Transaction) error { if !txProc.enableEpochsHandler.IsFlagEnabled(common.RelayedTransactionsV3Flag) { - return vmcommon.UserError, txProc.executingFailedTransactionRelayedV3(tx, sndAccount, relayerAccount, process.ErrRelayedTxV3Disabled) + return fmt.Errorf("%w, %s", process.ErrTransactionNotExecutable, process.ErrRelayedTxV3Disabled) } if !txProc.shardCoordinator.SameShard(tx.RelayerAddr, tx.SndAddr) { - return vmcommon.UserError, txProc.executingFailedTransactionRelayedV3(tx, sndAccount, relayerAccount, process.ErrShardIdMissmatch) + return fmt.Errorf("%w, %s", process.ErrTransactionNotExecutable, process.ErrShardIdMissmatch) } - if !check.IfNil(relayerAccount) && relayerAccount.IsGuarded() { - return vmcommon.UserError, txProc.executingFailedTransactionRelayedV3(tx, sndAccount, relayerAccount, process.ErrGuardedRelayerNotAllowed) + if bytes.Equal(tx.RelayerAddr, tx.GuardianAddr) { + return fmt.Errorf("%w, %s", process.ErrTransactionNotExecutable, process.ErrRelayedByGuardianNotAllowed) } - if bytes.Equal(tx.RelayerAddr, tx.GuardianAddr) { - return vmcommon.UserError, txProc.executingFailedTransactionRelayedV3(tx, sndAccount, relayerAccount, process.ErrRelayedByGuardianNotAllowed) + relayerAccount, err := txProc.getAccountFromAddress(tx.RelayerAddr) + if err != nil { + return fmt.Errorf("%w, %s", process.ErrTransactionNotExecutable, err) } - userTx := *tx - // remove relayer signature for tx type handler - // hash of this user tx won't be computed/used, but the originalTxHash - userTx.RelayerSignature = nil - minGasLimit := txProc.economicsFee.MinGasLimit() - userTx.GasLimit = userTx.GasLimit - minGasLimit - if userTx.GasLimit < txProc.economicsFee.ComputeGasLimit(&userTx) { - return vmcommon.UserError, txProc.executingFailedTransaction(tx, relayerAccount, process.ErrInsufficientGasLimitInTx) + if !check.IfNil(relayerAccount) && relayerAccount.IsGuarded() { + return fmt.Errorf("%w, %s", process.ErrTransactionNotExecutable, process.ErrGuardedRelayerNotAllowed) } - return txProc.finishExecutionOfRelayedTxV3(relayerAccount, tx, &userTx) + return nil } func (txProc *txProcessor) processRelayedTxV2( @@ -1133,28 +855,6 @@ func (txProc *txProcessor) removeValueAndConsumedFeeFromUser( return nil } -func (txProc *txProcessor) increaseSenderNonceIfNeededAndLog( - originalTxHash []byte, - originalTx *transaction.Transaction, - senderAccount vmcommon.AccountHandler, - executionErr error, -) error { - if txProc.shouldIncreaseNonce(executionErr) { - if check.IfNil(senderAccount) { - return process.ErrNilUserAccount - } - - senderAccount.IncreaseNonce(1) - - err := txProc.accounts.SaveAccount(senderAccount) - if err != nil { - return err - } - } - - return txProc.addNonExecutableLog(executionErr, originalTxHash, originalTx) -} - func (txProc *txProcessor) addNonExecutableLog(executionErr error, originalTxHash []byte, originalTx data.TransactionHandler) error { if !isNonExecutableError(executionErr) { return nil @@ -1215,7 +915,7 @@ func (txProc *txProcessor) processUserTx( err.Error()) } - txType, dstShardTxType := txProc.txTypeHandler.ComputeTransactionType(userTx) + txType, dstShardTxType, _ := txProc.txTypeHandler.ComputeTransactionType(userTx) err = txProc.checkTxValues(userTx, acntSnd, acntDst, true) if err != nil { errRemove := txProc.removeValueAndConsumedFeeFromUser(userTx, relayedTxValue, originalTxHash, originalTx, err) @@ -1240,7 +940,7 @@ func (txProc *txProcessor) processUserTx( returnCode := vmcommon.Ok switch txType { case process.MoveBalance: - err = txProc.processMoveBalance(userTx, acntSnd, acntDst, dstShardTxType, originalTxHash, true, false) + err = txProc.processMoveBalance(userTx, acntSnd, acntDst, dstShardTxType, originalTxHash, true) case process.SCDeployment: err = txProc.processMoveBalanceCostRelayedUserTx(userTx, scrFromTx, acntSnd, originalTxHash) if err != nil { @@ -1429,43 +1129,6 @@ func (txProc *txProcessor) executeFailedRelayedUserTx( return nil } -func (txProc *txProcessor) executeFailedRelayedV3UserTx( - originalTx *transaction.Transaction, - originalTxHash []byte, - errorMsg string, -) error { - - scrForRelayer := &smartContractResult.SmartContractResult{ - Nonce: originalTx.Nonce, - Value: big.NewInt(0), - RcvAddr: originalTx.RelayerAddr, - SndAddr: originalTx.SndAddr, - PrevTxHash: originalTxHash, - OriginalTxHash: originalTxHash, - ReturnMessage: []byte(errorMsg), - } - - err := txProc.scrForwarder.AddIntermediateTransactions([]data.TransactionHandler{scrForRelayer}, originalTxHash) - if err != nil { - return err - } - - moveBalanceGasLimit := txProc.economicsFee.ComputeGasLimit(originalTx) - gasToUse := originalTx.GetGasLimit() - moveBalanceGasLimit - processingUserFee := txProc.economicsFee.ComputeFeeForProcessing(originalTx, gasToUse) - moveBalanceUserFee := txProc.economicsFee.ComputeMoveBalanceFee(originalTx) - totalFee := big.NewInt(0).Add(moveBalanceUserFee, processingUserFee) - - senderShardID := txProc.shardCoordinator.ComputeId(originalTx.SndAddr) - if senderShardID != txProc.shardCoordinator.SelfId() { - totalFee.Sub(totalFee, processingUserFee) - } - - txProc.txFeeHandler.ProcessTransactionFee(totalFee, big.NewInt(0), originalTxHash) - - return txProc.badTxForwarder.AddIntermediateTransactions([]data.TransactionHandler{originalTx}, originalTxHash) -} - func (txProc *txProcessor) shouldIncreaseNonce(executionErr error) bool { if !txProc.enableEpochsHandler.IsFlagEnabled(common.RelayedNonceFixFlag) { return true diff --git a/process/transaction/shardProcess_test.go b/process/transaction/shardProcess_test.go index f65aadffb5f..905a6fc967b 100644 --- a/process/transaction/shardProcess_test.go +++ b/process/transaction/shardProcess_test.go @@ -1111,8 +1111,8 @@ func TestTxProcessor_ProcessTransactionScDeployTxShouldWork(t *testing.T) { args.Accounts = adb args.ScProcessor = scProcessorMock args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (transactionType process.TransactionType, destinationTransactionType process.TransactionType) { - return process.SCDeployment, process.SCDeployment + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (transactionType process.TransactionType, destinationTransactionType process.TransactionType, isRelayedV3 bool) { + return process.SCDeployment, process.SCDeployment, false }, } execTx, _ := txproc.NewTxProcessor(args) @@ -1159,8 +1159,8 @@ func TestTxProcessor_ProcessTransactionBuiltInFunctionCallShouldWork(t *testing. args.Accounts = adb args.ScProcessor = scProcessorMock args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (transactionType, destinationTransactionType process.TransactionType) { - return process.BuiltInFunctionCall, process.BuiltInFunctionCall + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (transactionType, destinationTransactionType process.TransactionType, isRelayedV3 bool) { + return process.BuiltInFunctionCall, process.BuiltInFunctionCall, false }, } execTx, _ := txproc.NewTxProcessor(args) @@ -1207,8 +1207,8 @@ func TestTxProcessor_ProcessTransactionScTxShouldWork(t *testing.T) { args.Accounts = adb args.ScProcessor = scProcessorMock args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.SCInvoking, process.SCInvoking + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.SCInvoking, process.SCInvoking, false }, } execTx, _ := txproc.NewTxProcessor(args) @@ -1253,8 +1253,8 @@ func TestTxProcessor_ProcessTransactionScTxShouldReturnErrWhenExecutionFails(t * args.Accounts = adb args.ScProcessor = scProcessorMock args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.SCInvoking, process.SCInvoking + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.SCInvoking, process.SCInvoking, false }, } execTx, _ := txproc.NewTxProcessor(args) @@ -1573,8 +1573,8 @@ func TestTxProcessor_ProcessTransactionShouldReturnErrForInvalidMetaTx(t *testin }, } args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.MoveBalance, process.MoveBalance + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.MoveBalance, process.MoveBalance, false }, } args.EnableEpochsHandler = enableEpochsHandlerMock.NewEnableEpochsHandlerStub(common.MetaProtectionFlag) @@ -1621,8 +1621,8 @@ func TestTxProcessor_ProcessTransactionShouldTreatAsInvalidTxIfTxTypeIsWrong(t * }, } args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.InvalidTransaction, process.InvalidTransaction + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.InvalidTransaction, process.InvalidTransaction, false }, } execTx, _ := txproc.NewTxProcessor(args) @@ -2181,8 +2181,8 @@ func TestTxProcessor_ProcessRelayedTransactionArgsParserErrorShouldError(t *test return nil, errors.New("failure") } args.Accounts = adb - args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (transactionType, destinationTransactionType process.TransactionType) { - return process.RelayedTx, process.RelayedTx + args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (transactionType, destinationTransactionType process.TransactionType, isRelayedV3 bool) { + return process.RelayedTx, process.RelayedTx, false }} execTx, _ := txproc.NewTxProcessor(args) @@ -2244,8 +2244,8 @@ func TestTxProcessor_ProcessRelayedTransactionMultipleArgumentsShouldError(t *te return nil, errors.New("failure") } args.Accounts = adb - args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (transactionType, destinationTransactionType process.TransactionType) { - return process.RelayedTx, process.RelayedTx + args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (transactionType, destinationTransactionType process.TransactionType, isRelayedV3 bool) { + return process.RelayedTx, process.RelayedTx, false }} execTx, _ := txproc.NewTxProcessor(args) @@ -2307,8 +2307,8 @@ func TestTxProcessor_ProcessRelayedTransactionFailUnMarshalInnerShouldError(t *t return nil, errors.New("failure") } args.Accounts = adb - args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (transactionType, destinationTransactionType process.TransactionType) { - return process.RelayedTx, process.RelayedTx + args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (transactionType, destinationTransactionType process.TransactionType, isRelayedV3 bool) { + return process.RelayedTx, process.RelayedTx, false }} execTx, _ := txproc.NewTxProcessor(args) @@ -2370,8 +2370,8 @@ func TestTxProcessor_ProcessRelayedTransactionDifferentSenderInInnerTxThanReceiv return nil, errors.New("failure") } args.Accounts = adb - args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (transactionType, destinationTransactionType process.TransactionType) { - return process.RelayedTx, process.RelayedTx + args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (transactionType, destinationTransactionType process.TransactionType, isRelayedV3 bool) { + return process.RelayedTx, process.RelayedTx, false }} execTx, _ := txproc.NewTxProcessor(args) @@ -2433,8 +2433,8 @@ func TestTxProcessor_ProcessRelayedTransactionSmallerValueInnerTxShouldError(t * return nil, errors.New("failure") } args.Accounts = adb - args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (transactionType, destinationTransactionType process.TransactionType) { - return process.RelayedTx, process.RelayedTx + args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (transactionType, destinationTransactionType process.TransactionType, isRelayedV3 bool) { + return process.RelayedTx, process.RelayedTx, false }} execTx, _ := txproc.NewTxProcessor(args) @@ -2496,8 +2496,8 @@ func TestTxProcessor_ProcessRelayedTransactionGasPriceMismatchShouldError(t *tes return nil, errors.New("failure") } args.Accounts = adb - args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (transactionType, destinationTransactionType process.TransactionType) { - return process.RelayedTx, process.RelayedTx + args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (transactionType, destinationTransactionType process.TransactionType, isRelayedV3 bool) { + return process.RelayedTx, process.RelayedTx, false }} execTx, _ := txproc.NewTxProcessor(args) @@ -2559,8 +2559,8 @@ func TestTxProcessor_ProcessRelayedTransactionGasLimitMismatchShouldError(t *tes return nil, errors.New("failure") } args.Accounts = adb - args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (transactionType, destinationTransactionType process.TransactionType) { - return process.RelayedTx, process.RelayedTx + args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (transactionType, destinationTransactionType process.TransactionType, isRelayedV3 bool) { + return process.RelayedTx, process.RelayedTx, false }} execTx, _ := txproc.NewTxProcessor(args) @@ -2912,8 +2912,8 @@ func TestTxProcessor_ProcessUserTxOfTypeRelayedShouldError(t *testing.T) { return nil, errors.New("failure") } args.Accounts = adb - args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (transactionType, destinationTransactionType process.TransactionType) { - return process.RelayedTx, process.RelayedTx + args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (transactionType, destinationTransactionType process.TransactionType, isRelayedV3 bool) { + return process.RelayedTx, process.RelayedTx, false }} execTx, _ := txproc.NewTxProcessor(args) @@ -2975,8 +2975,8 @@ func TestTxProcessor_ProcessUserTxOfTypeMoveBalanceShouldWork(t *testing.T) { return nil, errors.New("failure") } args.Accounts = adb - args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (transactionType, destinationTransactionType process.TransactionType) { - return process.MoveBalance, process.MoveBalance + args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (transactionType, destinationTransactionType process.TransactionType, isRelayedV3 bool) { + return process.MoveBalance, process.MoveBalance, false }} execTx, _ := txproc.NewTxProcessor(args) @@ -3038,8 +3038,8 @@ func TestTxProcessor_ProcessUserTxOfTypeSCDeploymentShouldWork(t *testing.T) { return nil, errors.New("failure") } args.Accounts = adb - args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (transactionType, destinationTransactionType process.TransactionType) { - return process.SCDeployment, process.SCDeployment + args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (transactionType, destinationTransactionType process.TransactionType, isRelayedV3 bool) { + return process.SCDeployment, process.SCDeployment, false }} execTx, _ := txproc.NewTxProcessor(args) @@ -3101,8 +3101,8 @@ func TestTxProcessor_ProcessUserTxOfTypeSCInvokingShouldWork(t *testing.T) { return nil, errors.New("failure") } args.Accounts = adb - args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (transactionType, destinationTransactionType process.TransactionType) { - return process.SCInvoking, process.SCInvoking + args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (transactionType, destinationTransactionType process.TransactionType, isRelayedV3 bool) { + return process.SCInvoking, process.SCInvoking, false }} execTx, _ := txproc.NewTxProcessor(args) @@ -3164,8 +3164,8 @@ func TestTxProcessor_ProcessUserTxOfTypeBuiltInFunctionCallShouldWork(t *testing return nil, errors.New("failure") } args.Accounts = adb - args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (transactionType, destinationTransactionType process.TransactionType) { - return process.BuiltInFunctionCall, process.BuiltInFunctionCall + args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (transactionType, destinationTransactionType process.TransactionType, isRelayedV3 bool) { + return process.BuiltInFunctionCall, process.BuiltInFunctionCall, false }} execTx, _ := txproc.NewTxProcessor(args) @@ -3231,8 +3231,8 @@ func TestTxProcessor_ProcessUserTxErrNotPayableShouldFailRelayTx(t *testing.T) { return nil, errors.New("failure") } args.Accounts = adb - args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (transactionType, destinationTransactionType process.TransactionType) { - return process.MoveBalance, process.MoveBalance + args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (transactionType, destinationTransactionType process.TransactionType, isRelayedV3 bool) { + return process.MoveBalance, process.MoveBalance, false }} execTx, _ := txproc.NewTxProcessor(args) @@ -3300,8 +3300,8 @@ func TestTxProcessor_ProcessUserTxFailedBuiltInFunctionCall(t *testing.T) { return nil, errors.New("failure") } args.Accounts = adb - args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (transactionType, destinationTransactionType process.TransactionType) { - return process.BuiltInFunctionCall, process.BuiltInFunctionCall + args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (transactionType, destinationTransactionType process.TransactionType, isRelayedV3 bool) { + return process.BuiltInFunctionCall, process.BuiltInFunctionCall, false }} execTx, _ := txproc.NewTxProcessor(args) @@ -3522,13 +3522,13 @@ func TestTxProcessor_ProcessMoveBalanceToNonPayableContract(t *testing.T) { args.SignMarshalizer = &marshaller cnt := 0 args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { cnt++ if cnt == 1 { - return process.RelayedTx, process.RelayedTx + return process.RelayedTx, process.RelayedTx, false } - return process.MoveBalance, process.MoveBalance + return process.MoveBalance, process.MoveBalance, false }, } args.EnableEpochsHandler = enableEpochsHandlerMock.NewEnableEpochsHandlerStub( diff --git a/process/transactionEvaluator/transactionEvaluator.go b/process/transactionEvaluator/transactionEvaluator.go index 9e61d138419..c2e566490ef 100644 --- a/process/transactionEvaluator/transactionEvaluator.go +++ b/process/transactionEvaluator/transactionEvaluator.go @@ -111,7 +111,7 @@ func (ate *apiTransactionEvaluator) ComputeTransactionGasLimit(tx *transaction.T ate.mutExecution.Unlock() }() - txTypeOnSender, txTypeOnDestination := ate.txTypeHandler.ComputeTransactionType(tx) + txTypeOnSender, txTypeOnDestination, _ := ate.txTypeHandler.ComputeTransactionType(tx) if txTypeOnSender == process.MoveBalance && txTypeOnDestination == process.MoveBalance { return ate.computeMoveBalanceCost(tx), nil } diff --git a/process/transactionEvaluator/transactionEvaluator_test.go b/process/transactionEvaluator/transactionEvaluator_test.go index f36a5388777..bfcbf97f787 100644 --- a/process/transactionEvaluator/transactionEvaluator_test.go +++ b/process/transactionEvaluator/transactionEvaluator_test.go @@ -114,8 +114,8 @@ func TestComputeTransactionGasLimit_MoveBalance(t *testing.T) { args := createArgs() args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.MoveBalance, process.MoveBalance + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.MoveBalance, process.MoveBalance, false }, } args.FeeHandler = &economicsmocks.EconomicsHandlerStub{ @@ -153,8 +153,8 @@ func TestComputeTransactionGasLimit_MoveBalanceInvalidNonceShouldStillComputeCos args := createArgs() args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.MoveBalance, process.MoveBalance + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.MoveBalance, process.MoveBalance, false }, } args.FeeHandler = &economicsmocks.EconomicsHandlerStub{ @@ -187,8 +187,8 @@ func TestComputeTransactionGasLimit_BuiltInFunction(t *testing.T) { consumedGasUnits := uint64(4000) args := createArgs() args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.BuiltInFunctionCall, process.BuiltInFunctionCall + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.BuiltInFunctionCall, process.BuiltInFunctionCall, false }, } args.FeeHandler = &economicsmocks.EconomicsHandlerStub{ @@ -223,8 +223,8 @@ func TestComputeTransactionGasLimit_BuiltInFunctionShouldErr(t *testing.T) { localErr := errors.New("local err") args := createArgs() args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.BuiltInFunctionCall, process.BuiltInFunctionCall + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.BuiltInFunctionCall, process.BuiltInFunctionCall, false }, } args.FeeHandler = &economicsmocks.EconomicsHandlerStub{ @@ -253,8 +253,8 @@ func TestComputeTransactionGasLimit_BuiltInFunctionShouldErr(t *testing.T) { func TestComputeTransactionGasLimit_NilVMOutput(t *testing.T) { args := createArgs() args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.BuiltInFunctionCall, process.BuiltInFunctionCall + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.BuiltInFunctionCall, process.BuiltInFunctionCall, false }, } args.FeeHandler = &economicsmocks.EconomicsHandlerStub{ @@ -284,8 +284,8 @@ func TestComputeTransactionGasLimit_NilVMOutput(t *testing.T) { func TestComputeTransactionGasLimit_RetCodeNotOk(t *testing.T) { args := createArgs() args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.BuiltInFunctionCall, process.BuiltInFunctionCall + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.BuiltInFunctionCall, process.BuiltInFunctionCall, false }, } args.FeeHandler = &economicsmocks.EconomicsHandlerStub{ @@ -321,8 +321,8 @@ func TestTransactionEvaluator_RelayedTxShouldErr(t *testing.T) { args := createArgs() args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.RelayedTx, process.RelayedTx + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.RelayedTx, process.RelayedTx, false }, } tce, _ := NewAPITransactionEvaluator(args) @@ -386,8 +386,8 @@ func TestApiTransactionEvaluator_ComputeTransactionGasLimit(t *testing.T) { _ = args.BlockChain.SetCurrentBlockHeaderAndRootHash(&block.Header{Nonce: expectedNonce}, []byte("test")) args.TxTypeHandler = &testscommon.TxTypeHandlerMock{ - ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { - return process.SCInvoking, process.SCInvoking + ComputeTransactionTypeCalled: func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.SCInvoking, process.SCInvoking, false }, } args.TxSimulator = &mock.TransactionSimulatorStub{ diff --git a/testscommon/scProcessorMock.go b/testscommon/scProcessorMock.go index 95e515b2950..ec96eb4c732 100644 --- a/testscommon/scProcessorMock.go +++ b/testscommon/scProcessorMock.go @@ -10,7 +10,7 @@ import ( // SCProcessorMock - type SCProcessorMock struct { - ComputeTransactionTypeCalled func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) + ComputeTransactionTypeCalled func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) ExecuteSmartContractTransactionCalled func(tx data.TransactionHandler, acntSrc, acntDst state.UserAccountHandler) (vmcommon.ReturnCode, error) ExecuteBuiltInFunctionCalled func(tx data.TransactionHandler, acntSrc, acntDst state.UserAccountHandler) (vmcommon.ReturnCode, error) DeploySmartContractCalled func(tx data.TransactionHandler, acntSrc state.UserAccountHandler) (vmcommon.ReturnCode, error) @@ -45,9 +45,9 @@ func (sc *SCProcessorMock) ProcessIfError( } // ComputeTransactionType - -func (sc *SCProcessorMock) ComputeTransactionType(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { +func (sc *SCProcessorMock) ComputeTransactionType(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { if sc.ComputeTransactionTypeCalled == nil { - return process.MoveBalance, process.MoveBalance + return process.MoveBalance, process.MoveBalance, false } return sc.ComputeTransactionTypeCalled(tx) diff --git a/testscommon/transactionCoordinatorMock.go b/testscommon/transactionCoordinatorMock.go index a1889b0b753..4aeac14dcff 100644 --- a/testscommon/transactionCoordinatorMock.go +++ b/testscommon/transactionCoordinatorMock.go @@ -12,7 +12,7 @@ import ( // TransactionCoordinatorMock - type TransactionCoordinatorMock struct { - ComputeTransactionTypeCalled func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) + ComputeTransactionTypeCalled func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) RequestMiniBlocksAndTransactionsCalled func(header data.HeaderHandler) RequestBlockTransactionsCalled func(body *block.Body) IsDataPreparedForProcessingCalled func(haveTime func() time.Duration) error @@ -57,9 +57,9 @@ func (tcm *TransactionCoordinatorMock) CreateReceiptsHash() ([]byte, error) { } // ComputeTransactionType - -func (tcm *TransactionCoordinatorMock) ComputeTransactionType(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { +func (tcm *TransactionCoordinatorMock) ComputeTransactionType(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { if tcm.ComputeTransactionTypeCalled == nil { - return 0, 0 + return 0, 0, false } return tcm.ComputeTransactionTypeCalled(tx) diff --git a/testscommon/txTypeHandlerMock.go b/testscommon/txTypeHandlerMock.go index 18a5a136477..bd08cc01c20 100644 --- a/testscommon/txTypeHandlerMock.go +++ b/testscommon/txTypeHandlerMock.go @@ -7,13 +7,13 @@ import ( // TxTypeHandlerMock - type TxTypeHandlerMock struct { - ComputeTransactionTypeCalled func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) + ComputeTransactionTypeCalled func(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) } // ComputeTransactionType - -func (th *TxTypeHandlerMock) ComputeTransactionType(tx data.TransactionHandler) (process.TransactionType, process.TransactionType) { +func (th *TxTypeHandlerMock) ComputeTransactionType(tx data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { if th.ComputeTransactionTypeCalled == nil { - return process.MoveBalance, process.MoveBalance + return process.MoveBalance, process.MoveBalance, false } return th.ComputeTransactionTypeCalled(tx) From 980496da00aca10ce7bc2028bd3f87b625396426 Mon Sep 17 00:00:00 2001 From: Sorin Stanculeanu Date: Mon, 2 Dec 2024 18:52:06 +0200 Subject: [PATCH 23/39] fix tests --- node/node_test.go | 2 +- process/transaction/shardProcess_test.go | 13 +++++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/node/node_test.go b/node/node_test.go index 48c1b115091..748749f5062 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -5307,7 +5307,7 @@ func getDefaultCoreComponents() *nodeMockFactory.CoreComponentsMock { StartTime: time.Time{}, EpochChangeNotifier: &epochNotifier.EpochNotifierStub{}, TxVersionCheckHandler: versioning.NewTxVersionChecker(0), - EnableEpochsHandlerField: &enableEpochsHandlerMock.EnableEpochsHandlerStub{}, + EnableEpochsHandlerField: enableEpochsHandlerMock.NewEnableEpochsHandlerStub(common.RelayedTransactionsV3Flag), } } diff --git a/process/transaction/shardProcess_test.go b/process/transaction/shardProcess_test.go index 905a6fc967b..75adbf3f3b9 100644 --- a/process/transaction/shardProcess_test.go +++ b/process/transaction/shardProcess_test.go @@ -7,6 +7,7 @@ import ( "errors" "fmt" "math/big" + "strings" "testing" "github.com/multiversx/mx-chain-core-go/core" @@ -2732,7 +2733,8 @@ func TestTxProcessor_ProcessRelayedTransactionV3(t *testing.T) { txCopy := *tx txCopy.Nonce = acntSrc.GetNonce() returnCode, err := txProcLocal.ProcessTransaction(&txCopy) - assert.Equal(t, process.ErrFailedTransaction, err) + assert.True(t, errors.Is(err, process.ErrTransactionNotExecutable)) + assert.True(t, strings.Contains(err.Error(), process.ErrRelayedTxV3Disabled.Error())) assert.Equal(t, vmcommon.UserError, returnCode) }) t.Run("relayer not in the same shard with the sender should error", func(t *testing.T) { @@ -2748,7 +2750,8 @@ func TestTxProcessor_ProcessRelayedTransactionV3(t *testing.T) { txCopy := *tx txCopy.Nonce = acntSrc.GetNonce() returnCode, err := txProcLocal.ProcessTransaction(&txCopy) - assert.Equal(t, process.ErrFailedTransaction, err) + assert.True(t, errors.Is(err, process.ErrTransactionNotExecutable)) + assert.True(t, strings.Contains(err.Error(), process.ErrShardIdMissmatch.Error())) assert.Equal(t, vmcommon.UserError, returnCode) }) t.Run("guarded relayer account should error", func(t *testing.T) { @@ -2781,7 +2784,8 @@ func TestTxProcessor_ProcessRelayedTransactionV3(t *testing.T) { txCopy := *tx txCopy.Nonce = acntSrc.GetNonce() returnCode, err := txProcLocal.ProcessTransaction(&txCopy) - assert.Equal(t, process.ErrFailedTransaction, err) + assert.True(t, errors.Is(err, process.ErrTransactionNotExecutable)) + assert.True(t, strings.Contains(err.Error(), process.ErrGuardedRelayerNotAllowed.Error())) assert.Equal(t, vmcommon.UserError, returnCode) }) t.Run("same guardian and relayer should error", func(t *testing.T) { @@ -2789,7 +2793,8 @@ func TestTxProcessor_ProcessRelayedTransactionV3(t *testing.T) { txCopy.Nonce = acntSrc.GetNonce() txCopy.GuardianAddr = txCopy.RelayerAddr returnCode, err := txProc.ProcessTransaction(&txCopy) - assert.Equal(t, process.ErrFailedTransaction, err) + assert.True(t, errors.Is(err, process.ErrTransactionNotExecutable)) + assert.True(t, strings.Contains(err.Error(), process.ErrRelayedByGuardianNotAllowed.Error())) assert.Equal(t, vmcommon.UserError, returnCode) }) t.Run("insufficient gas limit should error", func(t *testing.T) { From edc0ff3e0214bb9ad521c5f93e2a48eca06efe10 Mon Sep 17 00:00:00 2001 From: Sorin Stanculeanu Date: Tue, 3 Dec 2024 20:15:53 +0200 Subject: [PATCH 24/39] fixes after review --- common/common.go | 14 +++- common/common_test.go | 31 ++++++++- .../transactionsFeeProcessor.go | 2 +- process/coordinator/transactionType.go | 2 +- process/dataValidators/txValidator.go | 2 +- process/economics/economicsData.go | 2 +- process/errors.go | 3 + .../smartContract/processorV2/processV2.go | 6 +- process/transaction/baseProcess.go | 68 ++++++++----------- process/transaction/interceptedTransaction.go | 6 +- .../interceptedTransaction_test.go | 7 ++ 11 files changed, 95 insertions(+), 48 deletions(-) diff --git a/common/common.go b/common/common.go index 136c449d87d..c1e565043ad 100644 --- a/common/common.go +++ b/common/common.go @@ -2,7 +2,7 @@ package common import "github.com/multiversx/mx-chain-core-go/data" -// IsValidRelayedTxV3 returns true if the provided transaction a valid transaction of type relayed v3 +// IsValidRelayedTxV3 returns true if the provided transaction is a valid transaction of type relayed v3 func IsValidRelayedTxV3(tx data.TransactionHandler) bool { relayedTx, isRelayedV3 := tx.(data.RelayedTransactionHandler) if !isRelayedV3 { @@ -12,3 +12,15 @@ func IsValidRelayedTxV3(tx data.TransactionHandler) bool { hasValidRelayerSignature := len(relayedTx.GetRelayerSignature()) == len(relayedTx.GetSignature()) && len(relayedTx.GetRelayerSignature()) > 0 return hasValidRelayer && hasValidRelayerSignature } + +// IsRelayedTxV3 returns true if the provided transaction is a transaction of type relayed v3, without any further checks +func IsRelayedTxV3(tx data.TransactionHandler) bool { + relayedTx, isRelayedV3 := tx.(data.RelayedTransactionHandler) + if !isRelayedV3 { + return false + } + + hasRelayer := len(relayedTx.GetRelayerAddr()) > 0 + hasRelayerSignature := len(relayedTx.GetRelayerSignature()) > 0 + return hasRelayer || hasRelayerSignature +} diff --git a/common/common_test.go b/common/common_test.go index 1786a9d9421..5a0ec53a21f 100644 --- a/common/common_test.go +++ b/common/common_test.go @@ -9,11 +9,12 @@ import ( "github.com/stretchr/testify/require" ) -func TestIsRelayedTxV3(t *testing.T) { +func TestIsValidRelayedTxV3(t *testing.T) { t.Parallel() scr := &smartContractResult.SmartContractResult{} require.False(t, IsValidRelayedTxV3(scr)) + require.False(t, IsRelayedTxV3(scr)) notRelayedTxV3 := &transaction.Transaction{ Nonce: 1, @@ -25,6 +26,33 @@ func TestIsRelayedTxV3(t *testing.T) { Signature: []byte("signature"), } require.False(t, IsValidRelayedTxV3(notRelayedTxV3)) + require.False(t, IsRelayedTxV3(notRelayedTxV3)) + + invalidRelayedTxV3 := &transaction.Transaction{ + Nonce: 1, + Value: big.NewInt(100), + RcvAddr: []byte("receiver"), + SndAddr: []byte("sender0"), + GasPrice: 100, + GasLimit: 10, + Signature: []byte("signature"), + RelayerAddr: []byte("relayer"), + } + require.False(t, IsValidRelayedTxV3(invalidRelayedTxV3)) + require.True(t, IsRelayedTxV3(invalidRelayedTxV3)) + + invalidRelayedTxV3 = &transaction.Transaction{ + Nonce: 1, + Value: big.NewInt(100), + RcvAddr: []byte("receiver"), + SndAddr: []byte("sender0"), + GasPrice: 100, + GasLimit: 10, + Signature: []byte("signature"), + RelayerSignature: []byte("signature"), + } + require.False(t, IsValidRelayedTxV3(invalidRelayedTxV3)) + require.True(t, IsRelayedTxV3(invalidRelayedTxV3)) relayedTxV3 := &transaction.Transaction{ Nonce: 1, @@ -38,4 +66,5 @@ func TestIsRelayedTxV3(t *testing.T) { RelayerSignature: []byte("signature"), } require.True(t, IsValidRelayedTxV3(relayedTxV3)) + require.True(t, IsRelayedTxV3(relayedTxV3)) } diff --git a/outport/process/transactionsfee/transactionsFeeProcessor.go b/outport/process/transactionsfee/transactionsFeeProcessor.go index bbe1979b1b6..1d184f28152 100644 --- a/outport/process/transactionsfee/transactionsFeeProcessor.go +++ b/outport/process/transactionsfee/transactionsFeeProcessor.go @@ -272,7 +272,7 @@ func (tep *transactionsFeeProcessor) setGasUsedAndFeeBasedOnRefundValue( epoch uint32, ) { isValidUserTxAfterBaseCostActivation := !check.IfNil(userTx) && tep.enableEpochsHandler.IsFlagEnabledInEpoch(common.FixRelayedBaseCostFlag, epoch) - if isValidUserTxAfterBaseCostActivation && !common.IsValidRelayedTxV3(txWithResults.GetTxHandler()) { + if isValidUserTxAfterBaseCostActivation && !common.IsRelayedTxV3(txWithResults.GetTxHandler()) { gasUsed, fee := tep.txFeeCalculator.ComputeGasUsedAndFeeBasedOnRefundValue(userTx, refund) tx := txWithResults.GetTxHandler() diff --git a/process/coordinator/transactionType.go b/process/coordinator/transactionType.go index 871d670538c..45097d89c50 100644 --- a/process/coordinator/transactionType.go +++ b/process/coordinator/transactionType.go @@ -83,7 +83,7 @@ func (tth *txTypeHandler) ComputeTransactionType(tx data.TransactionHandler) (pr return process.InvalidTransaction, process.InvalidTransaction, false } - isRelayedV3 := common.IsValidRelayedTxV3(tx) + isRelayedV3 := common.IsRelayedTxV3(tx) isEmptyAddress := tth.isDestAddressEmpty(tx) if isEmptyAddress { diff --git a/process/dataValidators/txValidator.go b/process/dataValidators/txValidator.go index 414ce52c5b6..1e0c67ee007 100644 --- a/process/dataValidators/txValidator.go +++ b/process/dataValidators/txValidator.go @@ -109,7 +109,7 @@ func (txv *txValidator) getFeePayerAccount( payerAccount := accountHandler tx := interceptedTx.Transaction() - if common.IsValidRelayedTxV3(tx) { + if common.IsRelayedTxV3(tx) { relayedTx := tx.(data.RelayedTransactionHandler) payerAddress = relayedTx.GetRelayerAddr() relayerAccount, err := txv.accounts.GetExistingAccount(payerAddress) diff --git a/process/economics/economicsData.go b/process/economics/economicsData.go index 1e4ebe57454..dfce9d5a6f6 100644 --- a/process/economics/economicsData.go +++ b/process/economics/economicsData.go @@ -611,7 +611,7 @@ func (ed *economicsData) ComputeGasLimitBasedOnBalanceInEpoch(tx data.Transactio // getExtraGasLimitRelayedTx returns extra gas limit for relayed tx in a specific epoch func (ed *economicsData) getExtraGasLimitRelayedTx(txInstance *transaction.Transaction, epoch uint32) uint64 { - if common.IsValidRelayedTxV3(txInstance) { + if common.IsRelayedTxV3(txInstance) { return ed.MinGasLimitInEpoch(epoch) } diff --git a/process/errors.go b/process/errors.go index cd508052e61..feec446e096 100644 --- a/process/errors.go +++ b/process/errors.go @@ -1238,3 +1238,6 @@ var ErrGuardedRelayerNotAllowed = errors.New("guarded relayer not allowed") // ErrRelayedByGuardianNotAllowed signals that the provided guardian is also the relayer var ErrRelayedByGuardianNotAllowed = errors.New("relayed by guardian not allowed") + +// ErrInvalidRelayedTxV3 signals that an invalid relayed tx v3 has been provided +var ErrInvalidRelayedTxV3 = errors.New("invalid relayed transaction") diff --git a/process/smartContract/processorV2/processV2.go b/process/smartContract/processorV2/processV2.go index 0dd370cb665..7ac5d505c74 100644 --- a/process/smartContract/processorV2/processV2.go +++ b/process/smartContract/processorV2/processV2.go @@ -1685,7 +1685,7 @@ func getRelayedValues(tx data.TransactionHandler) ([]byte, *big.Int) { return relayedTx.RelayerAddr, big.NewInt(0) } - if common.IsValidRelayedTxV3(tx) { + if common.IsRelayedTxV3(tx) { relayedTx := tx.(data.RelayedTransactionHandler) return relayedTx.GetRelayerAddr(), big.NewInt(0) } @@ -1962,7 +1962,7 @@ func (sc *scProcessor) processSCPayment(tx data.TransactionHandler, acntSnd stat } func (sc *scProcessor) getFeePayer(tx data.TransactionHandler, acntSnd state.UserAccountHandler) (state.UserAccountHandler, error) { - if !common.IsValidRelayedTxV3(tx) { + if !common.IsRelayedTxV3(tx) { return acntSnd, nil } @@ -2654,7 +2654,7 @@ func (sc *scProcessor) createRefundGasToRelayerSCRIfNeeded( gasRefund) } - isRelayedV3 := common.IsValidRelayedTxV3(tx) + isRelayedV3 := common.IsRelayedTxV3(tx) shouldRefundGasToRelayerSCR = isRelayedV3 && callType != vmData.AsynchronousCall && gasRefund.Cmp(zero) > 0 if shouldRefundGasToRelayerSCR { relayedTx := tx.(data.RelayedTransactionHandler) diff --git a/process/transaction/baseProcess.go b/process/transaction/baseProcess.go index 5f745875547..1cb90d3bd2a 100644 --- a/process/transaction/baseProcess.go +++ b/process/transaction/baseProcess.go @@ -119,7 +119,11 @@ func (txProc *baseTxProcessor) checkTxValues( acntSnd, acntDst state.UserAccountHandler, isUserTxOfRelayed bool, ) error { - if common.IsValidRelayedTxV3(tx) { + if check.IfNil(acntSnd) { + return nil + } + + if common.IsRelayedTxV3(tx) { relayerAccount, err := txProc.getAccountFromAddress(tx.RelayerAddr) if err != nil { return err @@ -128,24 +132,7 @@ func (txProc *baseTxProcessor) checkTxValues( return txProc.checkUserTxOfRelayedV3Values(tx, acntSnd, acntDst, relayerAccount) } - err := txProc.verifyGuardian(tx, acntSnd) - if err != nil { - return err - } - err = txProc.checkUserNames(tx, acntSnd, acntDst) - if err != nil { - return err - } - if check.IfNil(acntSnd) { - return nil - } - if acntSnd.GetNonce() < tx.Nonce { - return process.ErrHigherNonceInTransaction - } - if acntSnd.GetNonce() > tx.Nonce { - return process.ErrLowerNonceInTransaction - } - err = txProc.economicsFee.CheckValidityTxValues(tx) + err := txProc.checkTxCommon(tx, acntSnd, acntDst) if err != nil { return err } @@ -189,24 +176,7 @@ func (txProc *baseTxProcessor) checkUserTxOfRelayedV3Values( destinationAccount state.UserAccountHandler, relayerAccount state.UserAccountHandler, ) error { - err := txProc.verifyGuardian(tx, senderAccount) - if err != nil { - return err - } - err = txProc.checkUserNames(tx, senderAccount, destinationAccount) - if err != nil { - return err - } - if check.IfNil(senderAccount) { - return nil - } - if senderAccount.GetNonce() < tx.Nonce { - return process.ErrHigherNonceInTransaction - } - if senderAccount.GetNonce() > tx.Nonce { - return process.ErrLowerNonceInTransaction - } - err = txProc.economicsFee.CheckValidityTxValues(tx) + err := txProc.checkTxCommon(tx, senderAccount, destinationAccount) if err != nil { return err } @@ -236,11 +206,33 @@ func (txProc *baseTxProcessor) checkUserTxOfRelayedV3Values( return nil } +func (txProc *baseTxProcessor) checkTxCommon( + tx *transaction.Transaction, + senderAccount state.UserAccountHandler, + destinationAccount state.UserAccountHandler, +) error { + err := txProc.verifyGuardian(tx, senderAccount) + if err != nil { + return err + } + err = txProc.checkUserNames(tx, senderAccount, destinationAccount) + if err != nil { + return err + } + if senderAccount.GetNonce() < tx.Nonce { + return process.ErrHigherNonceInTransaction + } + if senderAccount.GetNonce() > tx.Nonce { + return process.ErrLowerNonceInTransaction + } + return txProc.economicsFee.CheckValidityTxValues(tx) +} + func (txProc *baseTxProcessor) getFeePayer( tx *transaction.Transaction, acntSnd state.UserAccountHandler, ) (state.UserAccountHandler, bool, error) { - if !common.IsValidRelayedTxV3(tx) { + if !common.IsRelayedTxV3(tx) { return acntSnd, false, nil } diff --git a/process/transaction/interceptedTransaction.go b/process/transaction/interceptedTransaction.go index 533a0bf95c9..53722cad96f 100644 --- a/process/transaction/interceptedTransaction.go +++ b/process/transaction/interceptedTransaction.go @@ -240,7 +240,7 @@ func isRelayedTx(funcName string) bool { } func (inTx *InterceptedTransaction) verifyIfRelayedTxV3(tx *transaction.Transaction) error { - if !common.IsValidRelayedTxV3(tx) { + if !common.IsRelayedTxV3(tx) { return nil } @@ -248,6 +248,10 @@ func (inTx *InterceptedTransaction) verifyIfRelayedTxV3(tx *transaction.Transact return process.ErrRelayedTxV3Disabled } + if !common.IsValidRelayedTxV3(tx) { + return process.ErrInvalidRelayedTxV3 + } + err := inTx.integrity(tx) if err != nil { return err diff --git a/process/transaction/interceptedTransaction_test.go b/process/transaction/interceptedTransaction_test.go index 9e63b66d2e9..7459e586241 100644 --- a/process/transaction/interceptedTransaction_test.go +++ b/process/transaction/interceptedTransaction_test.go @@ -1585,7 +1585,14 @@ func TestInterceptedTransaction_CheckValidityOfRelayedTxV3(t *testing.T) { err = txi.CheckValidity() assert.Equal(t, process.ErrRelayedTxV3Disabled, err) + // invalid relayed v3 should error + tx.RelayerSignature = nil + txi, _ = createInterceptedTxFromPlainTxWithArgParser(tx) + err = txi.CheckValidity() + assert.Equal(t, process.ErrInvalidRelayedTxV3, err) + // sender in different shard than relayer should fail + tx.RelayerSignature = sigOk tx.RelayerAddr = bytes.Repeat([]byte("a"), len(relayerAddress)) txi, _ = createInterceptedTxFromPlainTxWithArgParser(tx) err = txi.CheckValidity() From 33568e5874f7963035d57b3f8309e32b14c8fc14 Mon Sep 17 00:00:00 2001 From: Sorin Stanculeanu Date: Tue, 3 Dec 2024 20:53:57 +0200 Subject: [PATCH 25/39] reverted common code in order to keep old logic on errors --- process/transaction/baseProcess.go | 63 +++++++++++++++++------------- 1 file changed, 36 insertions(+), 27 deletions(-) diff --git a/process/transaction/baseProcess.go b/process/transaction/baseProcess.go index 1cb90d3bd2a..c86c60339ff 100644 --- a/process/transaction/baseProcess.go +++ b/process/transaction/baseProcess.go @@ -119,9 +119,6 @@ func (txProc *baseTxProcessor) checkTxValues( acntSnd, acntDst state.UserAccountHandler, isUserTxOfRelayed bool, ) error { - if check.IfNil(acntSnd) { - return nil - } if common.IsRelayedTxV3(tx) { relayerAccount, err := txProc.getAccountFromAddress(tx.RelayerAddr) @@ -132,7 +129,24 @@ func (txProc *baseTxProcessor) checkTxValues( return txProc.checkUserTxOfRelayedV3Values(tx, acntSnd, acntDst, relayerAccount) } - err := txProc.checkTxCommon(tx, acntSnd, acntDst) + err := txProc.verifyGuardian(tx, acntSnd) + if err != nil { + return err + } + err = txProc.checkUserNames(tx, acntSnd, acntDst) + if err != nil { + return err + } + if check.IfNil(acntSnd) { + return nil + } + if acntSnd.GetNonce() < tx.Nonce { + return process.ErrHigherNonceInTransaction + } + if acntSnd.GetNonce() > tx.Nonce { + return process.ErrLowerNonceInTransaction + } + err = txProc.economicsFee.CheckValidityTxValues(tx) if err != nil { return err } @@ -176,7 +190,24 @@ func (txProc *baseTxProcessor) checkUserTxOfRelayedV3Values( destinationAccount state.UserAccountHandler, relayerAccount state.UserAccountHandler, ) error { - err := txProc.checkTxCommon(tx, senderAccount, destinationAccount) + err := txProc.verifyGuardian(tx, senderAccount) + if err != nil { + return err + } + err = txProc.checkUserNames(tx, senderAccount, destinationAccount) + if err != nil { + return err + } + if check.IfNil(senderAccount) { + return nil + } + if senderAccount.GetNonce() < tx.Nonce { + return process.ErrHigherNonceInTransaction + } + if senderAccount.GetNonce() > tx.Nonce { + return process.ErrLowerNonceInTransaction + } + err = txProc.economicsFee.CheckValidityTxValues(tx) if err != nil { return err } @@ -206,28 +237,6 @@ func (txProc *baseTxProcessor) checkUserTxOfRelayedV3Values( return nil } -func (txProc *baseTxProcessor) checkTxCommon( - tx *transaction.Transaction, - senderAccount state.UserAccountHandler, - destinationAccount state.UserAccountHandler, -) error { - err := txProc.verifyGuardian(tx, senderAccount) - if err != nil { - return err - } - err = txProc.checkUserNames(tx, senderAccount, destinationAccount) - if err != nil { - return err - } - if senderAccount.GetNonce() < tx.Nonce { - return process.ErrHigherNonceInTransaction - } - if senderAccount.GetNonce() > tx.Nonce { - return process.ErrLowerNonceInTransaction - } - return txProc.economicsFee.CheckValidityTxValues(tx) -} - func (txProc *baseTxProcessor) getFeePayer( tx *transaction.Transaction, acntSnd state.UserAccountHandler, From 0ac7c165c38fb73711e5c771cdadff5deb4a93f3 Mon Sep 17 00:00:00 2001 From: Sorin Stanculeanu Date: Wed, 4 Dec 2024 12:07:27 +0200 Subject: [PATCH 26/39] use proper prevTxHash in case of relayed v1 and v2 --- process/smartContract/processorV2/processV2.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/process/smartContract/processorV2/processV2.go b/process/smartContract/processorV2/processV2.go index 7ac5d505c74..31513ecea3b 100644 --- a/process/smartContract/processorV2/processV2.go +++ b/process/smartContract/processorV2/processV2.go @@ -2649,8 +2649,8 @@ func (sc *scProcessor) createRefundGasToRelayerSCRIfNeeded( relayedSCR.Nonce+1, relayedSCR.RelayerAddr, relayedSCR.OriginalSender, - relayedSCR.OriginalTxHash, txHash, + relayedSCR.OriginalTxHash, gasRefund) } From d744ac4e4f139f7c588777cf796ce5c263092ab7 Mon Sep 17 00:00:00 2001 From: Sorin Stanculeanu Date: Thu, 5 Dec 2024 11:56:04 +0200 Subject: [PATCH 27/39] fix transaction/pool endpoint issues --- .../transactionAPI/apiTransactionProcessor.go | 4 ++-- .../apiTransactionProcessor_test.go | 15 ++++++++++++++- node/external/transactionAPI/fieldsHandler.go | 5 ++++- .../external/transactionAPI/fieldsHandler_test.go | 2 ++ 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/node/external/transactionAPI/apiTransactionProcessor.go b/node/external/transactionAPI/apiTransactionProcessor.go index c67ad1cb445..2bd448829d1 100644 --- a/node/external/transactionAPI/apiTransactionProcessor.go +++ b/node/external/transactionAPI/apiTransactionProcessor.go @@ -394,8 +394,8 @@ func (atp *apiTransactionProcessor) getFieldGettersForTx(wrappedTx *txcache.Wrap rcvUsernameField: wrappedTx.Tx.GetRcvUserName(), dataField: wrappedTx.Tx.GetData(), valueField: getTxValue(wrappedTx), - senderShardID: wrappedTx.SenderShardID, - receiverShardID: wrappedTx.ReceiverShardID, + senderShardID: atp.shardCoordinator.ComputeId(wrappedTx.Tx.GetSndAddr()), + receiverShardID: atp.shardCoordinator.ComputeId(wrappedTx.Tx.GetRcvAddr()), } guardedTx, isGuardedTx := wrappedTx.Tx.(data.GuardedTransactionHandler) diff --git a/node/external/transactionAPI/apiTransactionProcessor_test.go b/node/external/transactionAPI/apiTransactionProcessor_test.go index e6a7040fe87..0282a6895ac 100644 --- a/node/external/transactionAPI/apiTransactionProcessor_test.go +++ b/node/external/transactionAPI/apiTransactionProcessor_test.go @@ -933,6 +933,9 @@ func TestApiTransactionProcessor_GetTransactionsPoolForSender(t *testing.T) { NumberOfShardsCalled: func() uint32 { return 1 }, + ComputeIdCalled: func(address []byte) uint32 { + return 1 // force to return different from 0 + }, } atp, err := NewAPITransactionProcessor(args) require.NoError(t, err) @@ -945,7 +948,17 @@ func TestApiTransactionProcessor_GetTransactionsPoolForSender(t *testing.T) { for i, tx := range res.Transactions { require.Equal(t, expectedHashes[i], tx.TxFields[hashField]) require.Equal(t, expectedValues[i], tx.TxFields[valueField]) - require.Equal(t, sender, tx.TxFields["sender"]) + require.Equal(t, sender, tx.TxFields[senderField]) + require.Equal(t, uint32(1), tx.TxFields[senderShardID]) + require.Equal(t, uint32(1), tx.TxFields[senderShardID]) + } + + res, err = atp.GetTransactionsPoolForSender(sender, "sender,value") // no hash, should be by default + require.NoError(t, err) + for i, tx := range res.Transactions { + require.Equal(t, expectedHashes[i], tx.TxFields[hashField]) + require.Equal(t, expectedValues[i], tx.TxFields[valueField]) + require.Equal(t, sender, tx.TxFields[senderField]) } // if no tx is found in pool for a sender, it isn't an error, but return empty slice diff --git a/node/external/transactionAPI/fieldsHandler.go b/node/external/transactionAPI/fieldsHandler.go index 4f837968cb7..4102d9ffc61 100644 --- a/node/external/transactionAPI/fieldsHandler.go +++ b/node/external/transactionAPI/fieldsHandler.go @@ -38,8 +38,11 @@ func newFieldsHandler(parameters string) fieldsHandler { } parameters = strings.ToLower(parameters) + fieldsMap := sliceToMap(strings.Split(parameters, separator)) + fieldsMap[hashField] = struct{}{} // hashField should always be returned + return fieldsHandler{ - fieldsMap: sliceToMap(strings.Split(parameters, separator)), + fieldsMap: fieldsMap, } } diff --git a/node/external/transactionAPI/fieldsHandler_test.go b/node/external/transactionAPI/fieldsHandler_test.go index fab3b3a41d9..0be9d0124b3 100644 --- a/node/external/transactionAPI/fieldsHandler_test.go +++ b/node/external/transactionAPI/fieldsHandler_test.go @@ -20,9 +20,11 @@ func Test_newFieldsHandler(t *testing.T) { for _, field := range splitFields { require.True(t, fh.IsFieldSet(field), fmt.Sprintf("field %s is not set", field)) } + require.True(t, fh.IsFieldSet(hashField), fmt.Sprintf("hashField should have been returned by default")) fh = newFieldsHandler("*") for _, field := range splitFields { require.True(t, fh.IsFieldSet(field)) } + require.True(t, fh.IsFieldSet(hashField), fmt.Sprintf("hashField should have been returned by default")) } From 9ac1370cac952b953b920d5da4b71d7009bd9636 Mon Sep 17 00:00:00 2001 From: Sorin Stanculeanu Date: Thu, 5 Dec 2024 12:07:30 +0200 Subject: [PATCH 28/39] fix linter --- node/external/transactionAPI/fieldsHandler_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/node/external/transactionAPI/fieldsHandler_test.go b/node/external/transactionAPI/fieldsHandler_test.go index 0be9d0124b3..75b3ae6f81a 100644 --- a/node/external/transactionAPI/fieldsHandler_test.go +++ b/node/external/transactionAPI/fieldsHandler_test.go @@ -20,11 +20,11 @@ func Test_newFieldsHandler(t *testing.T) { for _, field := range splitFields { require.True(t, fh.IsFieldSet(field), fmt.Sprintf("field %s is not set", field)) } - require.True(t, fh.IsFieldSet(hashField), fmt.Sprintf("hashField should have been returned by default")) + require.True(t, fh.IsFieldSet(hashField), "hashField should have been returned by default") fh = newFieldsHandler("*") for _, field := range splitFields { require.True(t, fh.IsFieldSet(field)) } - require.True(t, fh.IsFieldSet(hashField), fmt.Sprintf("hashField should have been returned by default")) + require.True(t, fh.IsFieldSet(hashField), "hashField should have been returned by default") } From 1bccd60e6a584f1746cc1a0e8fd9c456a098b196 Mon Sep 17 00:00:00 2001 From: Sorin Stanculeanu Date: Thu, 5 Dec 2024 12:28:09 +0200 Subject: [PATCH 29/39] update mx-chain-storage-go to tag --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 34e685d1b9f..cc5f4259b96 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,7 @@ require ( github.com/multiversx/mx-chain-es-indexer-go v1.7.10 github.com/multiversx/mx-chain-logger-go v1.0.15 github.com/multiversx/mx-chain-scenario-go v1.4.4 - github.com/multiversx/mx-chain-storage-go v1.0.18-0.20241202095036-f8323f446689 + github.com/multiversx/mx-chain-storage-go v1.0.18 github.com/multiversx/mx-chain-vm-common-go v1.5.16 github.com/multiversx/mx-chain-vm-go v1.5.37 github.com/multiversx/mx-chain-vm-v1_2-go v1.2.68 diff --git a/go.sum b/go.sum index f4e40f75a64..b4ebd7201e0 100644 --- a/go.sum +++ b/go.sum @@ -397,8 +397,8 @@ github.com/multiversx/mx-chain-logger-go v1.0.15 h1:HlNdK8etyJyL9NQ+6mIXyKPEBo+w github.com/multiversx/mx-chain-logger-go v1.0.15/go.mod h1:t3PRKaWB1M+i6gUfD27KXgzLJJC+mAQiN+FLlL1yoGQ= github.com/multiversx/mx-chain-scenario-go v1.4.4 h1:DVE2V+FPeyD/yWoC+KEfPK3jsFzHeruelESfpTlf460= github.com/multiversx/mx-chain-scenario-go v1.4.4/go.mod h1:kI+TWR3oIEgUkbwkHCPo2CQ3VjIge+ezGTibiSGwMxo= -github.com/multiversx/mx-chain-storage-go v1.0.18-0.20241202095036-f8323f446689 h1:qijedQ0WVc3ydsfNtMXbOiLgBc9Mw7iGzbhhrZVBY+0= -github.com/multiversx/mx-chain-storage-go v1.0.18-0.20241202095036-f8323f446689/go.mod h1:eFDEOrG7Wiyk5I/ObpwcN2eoBlOnnfeEMTvTer1cymk= +github.com/multiversx/mx-chain-storage-go v1.0.18 h1:DA33o5COEjnCKclCeCvzXXI0zIgFp2QqZK32UTVvDes= +github.com/multiversx/mx-chain-storage-go v1.0.18/go.mod h1:eFDEOrG7Wiyk5I/ObpwcN2eoBlOnnfeEMTvTer1cymk= github.com/multiversx/mx-chain-vm-common-go v1.5.16 h1:g1SqYjxl7K66Y1O/q6tvDJ37fzpzlxCSfRzSm/woQQY= github.com/multiversx/mx-chain-vm-common-go v1.5.16/go.mod h1:1rSkXreUZNXyPTTdhj47M+Fy62yjxbu3aAsXEtKN3UY= github.com/multiversx/mx-chain-vm-go v1.5.37 h1:Iy3KCvM+DOq1f9UPA7uYK/rI3ZbBOXc2CVNO2/vm5zw= From 353692799711feec685bc34be07de751ccc992b7 Mon Sep 17 00:00:00 2001 From: BeniaminDrasovean Date: Tue, 10 Dec 2024 16:28:31 +0200 Subject: [PATCH 30/39] remove old persisters when new already existing --- storage/pruning/pruningStorer.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/storage/pruning/pruningStorer.go b/storage/pruning/pruningStorer.go index 2007454a7c8..aab0b744e54 100644 --- a/storage/pruning/pruningStorer.go +++ b/storage/pruning/pruningStorer.go @@ -779,7 +779,7 @@ func (ps *PruningStorer) changeEpoch(header data.HeaderHandler) error { } log.Debug("change epoch pruning storer success", "persister", ps.identifier, "epoch", epoch) - return nil + return ps.removeOldPersistersIfNeeded(header, epoch) } shardID := core.GetShardIDString(ps.shardCoordinator.SelfId()) @@ -802,6 +802,10 @@ func (ps *PruningStorer) changeEpoch(header data.HeaderHandler) error { ps.activePersisters = append(singleItemPersisters, ps.activePersisters...) ps.persistersMapByEpoch[epoch] = newPersister + return ps.removeOldPersistersIfNeeded(header, epoch) +} + +func (ps *PruningStorer) removeOldPersistersIfNeeded(header data.HeaderHandler, epoch uint32) error { wasExtended := ps.extendSavedEpochsIfNeeded(header) if wasExtended { if len(ps.activePersisters) > int(ps.numOfActivePersisters) { @@ -814,11 +818,12 @@ func (ps *PruningStorer) changeEpoch(header data.HeaderHandler) error { return nil } - err = ps.closeAndDestroyPersisters(epoch) + err := ps.closeAndDestroyPersisters(epoch) if err != nil { log.Warn("closing persisters", "error", err.Error()) return err } + return nil } From aa66f652193d15790ce8dfb72ccf6b31cf088be7 Mon Sep 17 00:00:00 2001 From: BeniaminDrasovean Date: Tue, 10 Dec 2024 17:07:43 +0200 Subject: [PATCH 31/39] add log trace --- storage/pruning/fullHistoryPruningStorer.go | 1 + 1 file changed, 1 insertion(+) diff --git a/storage/pruning/fullHistoryPruningStorer.go b/storage/pruning/fullHistoryPruningStorer.go index 71213b1dcdd..97852aa3bcd 100644 --- a/storage/pruning/fullHistoryPruningStorer.go +++ b/storage/pruning/fullHistoryPruningStorer.go @@ -184,6 +184,7 @@ func (fhps *FullHistoryPruningStorer) getOrOpenPersister(epoch uint32) (storage. } fhps.oldEpochsActivePersistersCache.Put([]byte(epochString), newPdata, 0) + log.Trace("full history pruning storer - init new storer", "epoch", epoch) fhps.persistersMapByEpoch[epoch] = newPdata return newPdata.getPersister(), nil From 480ce7e2fcce9196fef6ce815347936d35b796fa Mon Sep 17 00:00:00 2001 From: Sorin Stanculeanu Date: Wed, 11 Dec 2024 09:36:55 +0200 Subject: [PATCH 32/39] fixes after merge --- process/transaction/baseProcess.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/process/transaction/baseProcess.go b/process/transaction/baseProcess.go index 77761dbf91c..73af11f5063 100644 --- a/process/transaction/baseProcess.go +++ b/process/transaction/baseProcess.go @@ -190,7 +190,7 @@ func (txProc *baseTxProcessor) checkUserTxOfRelayedV3Values( destinationAccount state.UserAccountHandler, relayerAccount state.UserAccountHandler, ) error { - err := txProc.verifyGuardian(tx, senderAccount) + err := txProc.VerifyGuardian(tx, senderAccount) if err != nil { return err } @@ -367,6 +367,7 @@ func (txProc *baseTxProcessor) checkGuardedAccountUnguardedTxPermission(tx *tran return nil } +// VerifyGuardian does the guardian verification func (txProc *baseTxProcessor) VerifyGuardian(tx *transaction.Transaction, account state.UserAccountHandler) error { if check.IfNil(account) { return nil From 0bdd1c3f50446c8db3384fba8d2059f55b2cfa5e Mon Sep 17 00:00:00 2001 From: BeniaminDrasovean Date: Wed, 11 Dec 2024 13:38:28 +0200 Subject: [PATCH 33/39] add unit tests --- .../pruning/fullHistoryPruningStorer_test.go | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/storage/pruning/fullHistoryPruningStorer_test.go b/storage/pruning/fullHistoryPruningStorer_test.go index 0e0d43877e8..80b0290511d 100644 --- a/storage/pruning/fullHistoryPruningStorer_test.go +++ b/storage/pruning/fullHistoryPruningStorer_test.go @@ -4,6 +4,7 @@ import ( "context" "crypto/rand" "fmt" + "github.com/multiversx/mx-chain-go/testscommon" "math" "path/filepath" "sync" @@ -399,3 +400,33 @@ func TestFullHistoryPruningStorer_IsInterfaceNil(t *testing.T) { fhps, _ = pruning.NewFullHistoryPruningStorer(fhArgs) require.False(t, fhps.IsInterfaceNil()) } + +func TestFullHistoryPruningStorer_changeEpochClosesOldDbs(t *testing.T) { + t.Parallel() + + shouldCleanCalled := false + args := getDefaultArgs() + fhArgs := pruning.FullHistoryStorerArgs{ + StorerArgs: args, + NumOfOldActivePersisters: 2, + } + fhArgs.OldDataCleanerProvider = &testscommon.OldDataCleanerProviderStub{ + ShouldCleanCalled: func() bool { + shouldCleanCalled = true + return true + }, + } + fhps, err := pruning.NewFullHistoryPruningStorer(fhArgs) + require.Nil(t, err) + + numEpochsChanged := 10 + startEpoch := uint32(0) + for i := 0; i < numEpochsChanged; i++ { + startEpoch++ + key := []byte(fmt.Sprintf("key-%d", i)) + _, _ = fhps.GetFromEpoch(key, startEpoch) + err = fhps.ChangeEpochSimple(startEpoch) + require.Nil(t, err) + } + require.True(t, shouldCleanCalled) +} From 1beb4ca88778ac0dda512234f45df59107e3c3a5 Mon Sep 17 00:00:00 2001 From: BeniaminDrasovean Date: Wed, 11 Dec 2024 13:41:15 +0200 Subject: [PATCH 34/39] sort imports --- storage/pruning/fullHistoryPruningStorer_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/pruning/fullHistoryPruningStorer_test.go b/storage/pruning/fullHistoryPruningStorer_test.go index 80b0290511d..d1274499bb9 100644 --- a/storage/pruning/fullHistoryPruningStorer_test.go +++ b/storage/pruning/fullHistoryPruningStorer_test.go @@ -4,7 +4,6 @@ import ( "context" "crypto/rand" "fmt" - "github.com/multiversx/mx-chain-go/testscommon" "math" "path/filepath" "sync" @@ -19,6 +18,7 @@ import ( "github.com/multiversx/mx-chain-go/storage/factory" "github.com/multiversx/mx-chain-go/storage/pathmanager" "github.com/multiversx/mx-chain-go/storage/pruning" + "github.com/multiversx/mx-chain-go/testscommon" logger "github.com/multiversx/mx-chain-logger-go" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" From fee796fb44c15303b33de173a46151c118f8154e Mon Sep 17 00:00:00 2001 From: BeniaminDrasovean Date: Wed, 11 Dec 2024 15:03:36 +0200 Subject: [PATCH 35/39] fix after review --- storage/pruning/pruningStorer.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/storage/pruning/pruningStorer.go b/storage/pruning/pruningStorer.go index aab0b744e54..d40680e5c87 100644 --- a/storage/pruning/pruningStorer.go +++ b/storage/pruning/pruningStorer.go @@ -779,7 +779,7 @@ func (ps *PruningStorer) changeEpoch(header data.HeaderHandler) error { } log.Debug("change epoch pruning storer success", "persister", ps.identifier, "epoch", epoch) - return ps.removeOldPersistersIfNeeded(header, epoch) + return ps.removeOldPersistersIfNeeded(header) } shardID := core.GetShardIDString(ps.shardCoordinator.SelfId()) @@ -802,10 +802,11 @@ func (ps *PruningStorer) changeEpoch(header data.HeaderHandler) error { ps.activePersisters = append(singleItemPersisters, ps.activePersisters...) ps.persistersMapByEpoch[epoch] = newPersister - return ps.removeOldPersistersIfNeeded(header, epoch) + return ps.removeOldPersistersIfNeeded(header) } -func (ps *PruningStorer) removeOldPersistersIfNeeded(header data.HeaderHandler, epoch uint32) error { +func (ps *PruningStorer) removeOldPersistersIfNeeded(header data.HeaderHandler) error { + epoch := header.GetEpoch() wasExtended := ps.extendSavedEpochsIfNeeded(header) if wasExtended { if len(ps.activePersisters) > int(ps.numOfActivePersisters) { From 05e3a2a325185d135ca7fa16f45efefe2f545831 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrei=20B=C4=83ncioiu?= Date: Thu, 12 Dec 2024 14:30:42 +0200 Subject: [PATCH 36/39] Integrate storage-go. --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 538474732a6..70173c016bc 100644 --- a/go.mod +++ b/go.mod @@ -15,12 +15,12 @@ require ( github.com/klauspost/cpuid/v2 v2.2.5 github.com/mitchellh/mapstructure v1.5.0 github.com/multiversx/mx-chain-communication-go v1.1.1 - github.com/multiversx/mx-chain-core-go v1.2.24-0.20241029140551-8ed69b598c83 + github.com/multiversx/mx-chain-core-go v1.2.24-0.20241204105653-2beb13136490 github.com/multiversx/mx-chain-crypto-go v1.2.12 github.com/multiversx/mx-chain-es-indexer-go v1.7.11-0.20241118100151-956a1f23c5c1 github.com/multiversx/mx-chain-logger-go v1.0.15 github.com/multiversx/mx-chain-scenario-go v1.4.4 - github.com/multiversx/mx-chain-storage-go v1.0.18 + github.com/multiversx/mx-chain-storage-go v1.0.19-0.20241212122833-853f67604809 github.com/multiversx/mx-chain-vm-common-go v1.5.16 github.com/multiversx/mx-chain-vm-go v1.5.37 github.com/multiversx/mx-chain-vm-v1_2-go v1.2.68 diff --git a/go.sum b/go.sum index f06c97b4198..d47072eb254 100644 --- a/go.sum +++ b/go.sum @@ -387,8 +387,8 @@ github.com/multiversx/concurrent-map v0.1.4 h1:hdnbM8VE4b0KYJaGY5yJS2aNIW9TFFsUY github.com/multiversx/concurrent-map v0.1.4/go.mod h1:8cWFRJDOrWHOTNSqgYCUvwT7c7eFQ4U2vKMOp4A/9+o= github.com/multiversx/mx-chain-communication-go v1.1.1 h1:y4DoQeQOJTaSUsRzczQFazf8JYQmInddypApqA3AkwM= github.com/multiversx/mx-chain-communication-go v1.1.1/go.mod h1:WK6bP4pGEHGDDna/AYRIMtl6G9OA0NByI1Lw8PmOnRM= -github.com/multiversx/mx-chain-core-go v1.2.24-0.20241029140551-8ed69b598c83 h1:VuFFYZ9hpMacAcqcKM0hg6j4D16qKAGihi3X6PaF8qs= -github.com/multiversx/mx-chain-core-go v1.2.24-0.20241029140551-8ed69b598c83/go.mod h1:B5zU4MFyJezmEzCsAHE9YNULmGCm2zbPHvl9hazNxmE= +github.com/multiversx/mx-chain-core-go v1.2.24-0.20241204105653-2beb13136490 h1:uK29uJdsvVYMp37wjC/qu74O8V04gFw0Bw7q9C9zc+c= +github.com/multiversx/mx-chain-core-go v1.2.24-0.20241204105653-2beb13136490/go.mod h1:B5zU4MFyJezmEzCsAHE9YNULmGCm2zbPHvl9hazNxmE= github.com/multiversx/mx-chain-crypto-go v1.2.12 h1:zWip7rpUS4CGthJxfKn5MZfMfYPjVjIiCID6uX5BSOk= github.com/multiversx/mx-chain-crypto-go v1.2.12/go.mod h1:HzcPpCm1zanNct/6h2rIh+MFrlXbjA5C8+uMyXj3LI4= github.com/multiversx/mx-chain-es-indexer-go v1.7.11-0.20241118100151-956a1f23c5c1 h1:wgMxgtUWd9//FPCTOLj/75j9Kwnd9PE2tHk0KLIFF6s= @@ -397,8 +397,8 @@ github.com/multiversx/mx-chain-logger-go v1.0.15 h1:HlNdK8etyJyL9NQ+6mIXyKPEBo+w github.com/multiversx/mx-chain-logger-go v1.0.15/go.mod h1:t3PRKaWB1M+i6gUfD27KXgzLJJC+mAQiN+FLlL1yoGQ= github.com/multiversx/mx-chain-scenario-go v1.4.4 h1:DVE2V+FPeyD/yWoC+KEfPK3jsFzHeruelESfpTlf460= github.com/multiversx/mx-chain-scenario-go v1.4.4/go.mod h1:kI+TWR3oIEgUkbwkHCPo2CQ3VjIge+ezGTibiSGwMxo= -github.com/multiversx/mx-chain-storage-go v1.0.18 h1:DA33o5COEjnCKclCeCvzXXI0zIgFp2QqZK32UTVvDes= -github.com/multiversx/mx-chain-storage-go v1.0.18/go.mod h1:eFDEOrG7Wiyk5I/ObpwcN2eoBlOnnfeEMTvTer1cymk= +github.com/multiversx/mx-chain-storage-go v1.0.19-0.20241212122833-853f67604809 h1:UUYjeDiQhj7geG6zvnx4rIR0Nx3K6Ahr2/HIMzKMarI= +github.com/multiversx/mx-chain-storage-go v1.0.19-0.20241212122833-853f67604809/go.mod h1:Ec+CrhDskz+UPcw/WjOCtQS4uCA1GNCseO3qM6SHj+A= github.com/multiversx/mx-chain-vm-common-go v1.5.16 h1:g1SqYjxl7K66Y1O/q6tvDJ37fzpzlxCSfRzSm/woQQY= github.com/multiversx/mx-chain-vm-common-go v1.5.16/go.mod h1:1rSkXreUZNXyPTTdhj47M+Fy62yjxbu3aAsXEtKN3UY= github.com/multiversx/mx-chain-vm-go v1.5.37 h1:Iy3KCvM+DOq1f9UPA7uYK/rI3ZbBOXc2CVNO2/vm5zw= From 559d93a043b86427ae2f9b554967e355073c2182 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrei=20B=C4=83ncioiu?= Date: Fri, 13 Dec 2024 17:04:06 +0200 Subject: [PATCH 37/39] Reference storage-go, fix mempool memory tests. --- dataRetriever/txpool/memorytests/memory_test.go | 6 +++--- go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/dataRetriever/txpool/memorytests/memory_test.go b/dataRetriever/txpool/memorytests/memory_test.go index 1359ae8fb5f..e4e7f5f68b8 100644 --- a/dataRetriever/txpool/memorytests/memory_test.go +++ b/dataRetriever/txpool/memorytests/memory_test.go @@ -53,9 +53,9 @@ func TestShardedTxPool_MemoryFootprint(t *testing.T) { journals = append(journals, runScenario(t, newScenario(100, 1, core.MegabyteSize, "1_0"), memoryAssertion{90, 100}, memoryAssertion{0, 1})) journals = append(journals, runScenario(t, newScenario(10000, 1, 10240, "1_0"), memoryAssertion{96, 128}, memoryAssertion{0, 4})) - journals = append(journals, runScenario(t, newScenario(10, 10000, 1000, "1_0"), memoryAssertion{96, 140}, memoryAssertion{16, 25})) - journals = append(journals, runScenario(t, newScenario(150000, 1, 128, "1_0"), memoryAssertion{50, 80}, memoryAssertion{30, 40})) - journals = append(journals, runScenario(t, newScenario(1, 150000, 128, "1_0"), memoryAssertion{50, 80}, memoryAssertion{30, 40})) + journals = append(journals, runScenario(t, newScenario(10, 10000, 1000, "1_0"), memoryAssertion{96, 148}, memoryAssertion{16, 32})) + journals = append(journals, runScenario(t, newScenario(150000, 1, 128, "1_0"), memoryAssertion{50, 84}, memoryAssertion{30, 48})) + journals = append(journals, runScenario(t, newScenario(1, 150000, 128, "1_0"), memoryAssertion{50, 84}, memoryAssertion{30, 48})) for _, journal := range journals { journal.displayFootprintsSummary() diff --git a/go.mod b/go.mod index 70173c016bc..eb5d88cfb61 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,7 @@ require ( github.com/multiversx/mx-chain-es-indexer-go v1.7.11-0.20241118100151-956a1f23c5c1 github.com/multiversx/mx-chain-logger-go v1.0.15 github.com/multiversx/mx-chain-scenario-go v1.4.4 - github.com/multiversx/mx-chain-storage-go v1.0.19-0.20241212122833-853f67604809 + github.com/multiversx/mx-chain-storage-go v1.0.19-0.20241213090416-f46569554341 github.com/multiversx/mx-chain-vm-common-go v1.5.16 github.com/multiversx/mx-chain-vm-go v1.5.37 github.com/multiversx/mx-chain-vm-v1_2-go v1.2.68 diff --git a/go.sum b/go.sum index d47072eb254..f96330123f3 100644 --- a/go.sum +++ b/go.sum @@ -397,8 +397,8 @@ github.com/multiversx/mx-chain-logger-go v1.0.15 h1:HlNdK8etyJyL9NQ+6mIXyKPEBo+w github.com/multiversx/mx-chain-logger-go v1.0.15/go.mod h1:t3PRKaWB1M+i6gUfD27KXgzLJJC+mAQiN+FLlL1yoGQ= github.com/multiversx/mx-chain-scenario-go v1.4.4 h1:DVE2V+FPeyD/yWoC+KEfPK3jsFzHeruelESfpTlf460= github.com/multiversx/mx-chain-scenario-go v1.4.4/go.mod h1:kI+TWR3oIEgUkbwkHCPo2CQ3VjIge+ezGTibiSGwMxo= -github.com/multiversx/mx-chain-storage-go v1.0.19-0.20241212122833-853f67604809 h1:UUYjeDiQhj7geG6zvnx4rIR0Nx3K6Ahr2/HIMzKMarI= -github.com/multiversx/mx-chain-storage-go v1.0.19-0.20241212122833-853f67604809/go.mod h1:Ec+CrhDskz+UPcw/WjOCtQS4uCA1GNCseO3qM6SHj+A= +github.com/multiversx/mx-chain-storage-go v1.0.19-0.20241213090416-f46569554341 h1:SydNXPZIt7UpcveL8mUnOGAh+Oped851w2bGbaGqsWw= +github.com/multiversx/mx-chain-storage-go v1.0.19-0.20241213090416-f46569554341/go.mod h1:Ec+CrhDskz+UPcw/WjOCtQS4uCA1GNCseO3qM6SHj+A= github.com/multiversx/mx-chain-vm-common-go v1.5.16 h1:g1SqYjxl7K66Y1O/q6tvDJ37fzpzlxCSfRzSm/woQQY= github.com/multiversx/mx-chain-vm-common-go v1.5.16/go.mod h1:1rSkXreUZNXyPTTdhj47M+Fy62yjxbu3aAsXEtKN3UY= github.com/multiversx/mx-chain-vm-go v1.5.37 h1:Iy3KCvM+DOq1f9UPA7uYK/rI3ZbBOXc2CVNO2/vm5zw= From 8e08e1a3630e994cfd84b88d73d09aa649e5eab2 Mon Sep 17 00:00:00 2001 From: Sorin Stanculeanu Date: Fri, 13 Dec 2024 17:59:28 +0200 Subject: [PATCH 38/39] accept relayed tx v3 with sender account non-existent --- .../relayedTx/relayedTx_test.go | 55 +++++++++++++++++-- process/dataValidators/txValidator.go | 22 +++++++- process/dataValidators/txValidator_test.go | 7 ++- 3 files changed, 74 insertions(+), 10 deletions(-) diff --git a/integrationTests/chainSimulator/relayedTx/relayedTx_test.go b/integrationTests/chainSimulator/relayedTx/relayedTx_test.go index 57c4393e1a4..f1b6582f83b 100644 --- a/integrationTests/chainSimulator/relayedTx/relayedTx_test.go +++ b/integrationTests/chainSimulator/relayedTx/relayedTx_test.go @@ -1,6 +1,7 @@ package relayedTx import ( + "crypto/rand" "encoding/hex" "encoding/json" "math/big" @@ -20,7 +21,9 @@ import ( "github.com/multiversx/mx-chain-go/node/chainSimulator/dtos" chainSimulatorProcess "github.com/multiversx/mx-chain-go/node/chainSimulator/process" "github.com/multiversx/mx-chain-go/process" + "github.com/multiversx/mx-chain-go/sharding" "github.com/multiversx/mx-chain-go/vm" + logger "github.com/multiversx/mx-chain-logger-go" "github.com/stretchr/testify/require" ) @@ -55,8 +58,10 @@ func TestRelayedV3WithChainSimulator(t *testing.T) { t.Run("intra shard move balance, invalid gas", testRelayedV3MoveInvalidGasLimit(0, 0)) t.Run("cross shard move balance, invalid gas", testRelayedV3MoveInvalidGasLimit(0, 1)) - t.Run("successful intra shard sc call with refunds", testRelayedV3ScCall(0, 0)) - t.Run("successful cross shard sc call with refunds", testRelayedV3ScCall(0, 1)) + t.Run("successful intra shard sc call with refunds, existing sender", testRelayedV3ScCall(0, 0, true)) + t.Run("successful intra shard sc call with refunds, new sender", testRelayedV3ScCall(0, 0, false)) + t.Run("successful cross shard sc call with refunds, existing sender", testRelayedV3ScCall(0, 1, true)) + t.Run("successful cross shard sc call with refunds, new sender", testRelayedV3ScCall(0, 1, false)) t.Run("intra shard sc call, invalid gas", testRelayedV3ScCallInvalidGasLimit(0, 0)) t.Run("cross shard sc call, invalid gas", testRelayedV3ScCallInvalidGasLimit(0, 1)) t.Run("intra shard sc call, invalid method", testRelayedV3ScCallInvalidMethod(0, 0)) @@ -267,6 +272,7 @@ func testRelayedV3MoveInvalidGasLimit( func testRelayedV3ScCall( relayerShard uint32, ownerShard uint32, + existingSenderWithBalance bool, ) func(t *testing.T) { return func(t *testing.T) { if testing.Short() { @@ -286,8 +292,7 @@ func testRelayedV3ScCall( relayer, err := cs.GenerateAndMintWalletAddress(relayerShard, initialBalance) require.NoError(t, err) - sender, err := cs.GenerateAndMintWalletAddress(relayerShard, initialBalance) - require.NoError(t, err) + sender, senderInitialBalance := prepareSender(t, cs, existingSenderWithBalance, relayerShard, initialBalance) owner, err := cs.GenerateAndMintWalletAddress(ownerShard, initialBalance) require.NoError(t, err) @@ -330,7 +335,7 @@ func testRelayedV3ScCall( // check sender balance senderBalanceAfter := getBalance(t, cs, sender) - require.Equal(t, initialBalance.String(), senderBalanceAfter.String()) + require.Equal(t, senderInitialBalance.String(), senderBalanceAfter.String()) // check owner balance _, feeDeploy, _ := computeTxGasAndFeeBasedOnRefund(resultDeploy, refundDeploy, false, false) @@ -346,6 +351,46 @@ func testRelayedV3ScCall( } } +func prepareSender( + t *testing.T, + cs testsChainSimulator.ChainSimulator, + existingSenderWithBalance bool, + shard uint32, + initialBalance *big.Int, +) (dtos.WalletAddress, *big.Int) { + if existingSenderWithBalance { + sender, err := cs.GenerateAndMintWalletAddress(shard, initialBalance) + require.NoError(t, err) + + return sender, initialBalance + } + + shardC := cs.GetNodeHandler(shard).GetShardCoordinator() + pkConv := cs.GetNodeHandler(shard).GetCoreComponents().AddressPubKeyConverter() + newAddress := generateAddressInShard(shardC, pkConv.Len()) + return dtos.WalletAddress{ + Bech32: pkConv.SilentEncode(newAddress, logger.GetOrCreate("tmp")), + Bytes: newAddress, + }, big.NewInt(0) +} + +func generateAddressInShard(shardCoordinator sharding.Coordinator, len int) []byte { + for { + buff := generateAddress(len) + shardID := shardCoordinator.ComputeId(buff) + if shardID == shardCoordinator.SelfId() { + return buff + } + } +} + +func generateAddress(len int) []byte { + buff := make([]byte, len) + _, _ = rand.Read(buff) + + return buff +} + func testRelayedV3ScCallInvalidGasLimit( relayerShard uint32, ownerShard uint32, diff --git a/process/dataValidators/txValidator.go b/process/dataValidators/txValidator.go index 1e0c67ee007..c28b6265657 100644 --- a/process/dataValidators/txValidator.go +++ b/process/dataValidators/txValidator.go @@ -2,6 +2,7 @@ package dataValidators import ( "fmt" + "math/big" "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-core-go/core/check" @@ -76,14 +77,27 @@ func (txv *txValidator) CheckTxValidity(interceptedTx process.InterceptedTransac return nil } + // for relayed v3, we allow sender accounts that do not exist + isRelayedV3 := common.IsRelayedTxV3(interceptedTx.Transaction()) + hasValue := hasTxValue(interceptedTx) + shouldAllowMissingSenderAccount := isRelayedV3 && !hasValue accountHandler, err := txv.getSenderAccount(interceptedTx) - if err != nil { + if err != nil && !shouldAllowMissingSenderAccount { return err } return txv.checkAccount(interceptedTx, accountHandler) } +func hasTxValue(interceptedTx process.InterceptedTransactionHandler) bool { + txValue := interceptedTx.Transaction().GetValue() + if check.IfNilReflect(txValue) { + return false + } + + return big.NewInt(0).Cmp(txValue) < 0 +} + func (txv *txValidator) checkAccount( interceptedTx process.InterceptedTransactionHandler, accountHandler vmcommon.AccountHandler, @@ -149,7 +163,11 @@ func (txv *txValidator) checkBalance(interceptedTx process.InterceptedTransactio } func (txv *txValidator) checkNonce(interceptedTx process.InterceptedTransactionHandler, accountHandler vmcommon.AccountHandler) error { - accountNonce := accountHandler.GetNonce() + accountNonce := uint64(0) + if !check.IfNil(accountHandler) { + accountNonce = accountHandler.GetNonce() + } + txNonce := interceptedTx.Nonce() lowerNonceInTx := txNonce < accountNonce veryHighNonceInTx := txNonce > accountNonce+uint64(txv.maxNonceDeltaAllowed) diff --git a/process/dataValidators/txValidator_test.go b/process/dataValidators/txValidator_test.go index a45acf2b434..31fa230ec39 100644 --- a/process/dataValidators/txValidator_test.go +++ b/process/dataValidators/txValidator_test.go @@ -324,11 +324,11 @@ func TestTxValidator_CheckTxValidityAccountBalanceIsLessThanTxTotalValueShouldRe adb.GetExistingAccountCalled = func(address []byte) (handler vmcommon.AccountHandler, e error) { cnt++ if cnt == 1 { - require.True(t, bytes.Equal(providedSenderAddress, address)) - } else { - require.True(t, bytes.Equal(providedRelayerAddress, address)) + return nil, errors.New("sender not found") } + require.True(t, bytes.Equal(providedRelayerAddress, address)) + acc, _ := accounts.NewUserAccount(address, &trie.DataTrieTrackerStub{}, &trie.TrieLeafParserStub{}) acc.Nonce = accountNonce acc.Balance = accountBalance @@ -358,6 +358,7 @@ func TestTxValidator_CheckTxValidityAccountBalanceIsLessThanTxTotalValueShouldRe Signature: []byte("address sig"), RelayerAddr: providedRelayerAddress, RelayerSignature: []byte("relayer sig"), + Value: big.NewInt(0), } } result := txValidator.CheckTxValidity(txValidatorHandler) From 8cf26729f46d0a9b101e1b3f1b744c49b4fea647 Mon Sep 17 00:00:00 2001 From: Sorin Stanculeanu Date: Mon, 16 Dec 2024 09:53:22 +0200 Subject: [PATCH 39/39] fix after review --- process/dataValidators/txValidator.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/process/dataValidators/txValidator.go b/process/dataValidators/txValidator.go index c28b6265657..d043f207ac0 100644 --- a/process/dataValidators/txValidator.go +++ b/process/dataValidators/txValidator.go @@ -2,7 +2,6 @@ package dataValidators import ( "fmt" - "math/big" "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-core-go/core/check" @@ -95,7 +94,7 @@ func hasTxValue(interceptedTx process.InterceptedTransactionHandler) bool { return false } - return big.NewInt(0).Cmp(txValue) < 0 + return txValue.Sign() > 0 } func (txv *txValidator) checkAccount(