diff --git a/api/groups/blockGroup.go b/api/groups/blockGroup.go index 26d9c05b000..f9f0ed81fd8 100644 --- a/api/groups/blockGroup.go +++ b/api/groups/blockGroup.go @@ -26,6 +26,7 @@ const ( urlParamTokensFilter = "tokens" urlParamWithTxs = "withTxs" urlParamWithLogs = "withLogs" + urlParamForHyperblock = "forHyperblock" ) // blockFacadeHandler defines the methods to be implemented by a facade for handling block requests @@ -219,7 +220,12 @@ func parseBlockQueryOptions(c *gin.Context) (api.BlockQueryOptions, error) { return api.BlockQueryOptions{}, err } - options := api.BlockQueryOptions{WithTransactions: withTxs, WithLogs: withLogs} + forHyperBlock, err := parseBoolUrlParam(c, urlParamForHyperblock) + if err != nil { + return api.BlockQueryOptions{}, err + } + + options := api.BlockQueryOptions{WithTransactions: withTxs, WithLogs: withLogs, ForHyperblock: forHyperBlock} return options, nil } diff --git a/api/groups/blockGroup_test.go b/api/groups/blockGroup_test.go index b190c2f0561..a2ebc61548b 100644 --- a/api/groups/blockGroup_test.go +++ b/api/groups/blockGroup_test.go @@ -90,7 +90,7 @@ func TestBlockGroup_getBlockByNonce(t *testing.T) { t.Parallel() providedNonce := uint64(37) - expectedOptions := api.BlockQueryOptions{WithTransactions: true} + expectedOptions := api.BlockQueryOptions{WithTransactions: true, ForHyperblock: true} expectedBlock := api.Block{ Nonce: 37, Round: 39, @@ -107,7 +107,7 @@ func TestBlockGroup_getBlockByNonce(t *testing.T) { loadBlockGroupResponse( t, facade, - fmt.Sprintf("/block/by-nonce/%d?withTxs=true", providedNonce), + fmt.Sprintf("/block/by-nonce/%d?withTxs=true&forHyperblock=true", providedNonce), "GET", nil, response, diff --git a/api/groups/networkGroup.go b/api/groups/networkGroup.go index ae0cca6846d..8a8e7854f9d 100644 --- a/api/groups/networkGroup.go +++ b/api/groups/networkGroup.go @@ -117,7 +117,7 @@ func NewNetworkGroup(facade networkFacadeHandler) (*networkGroup, error) { { Path: getNFTsPath, Method: http.MethodGet, - Handler: ng.getHandlerFuncForEsdt(core.NonFungibleESDT), + Handler: ng.getHandlerFuncForEsdt(core.NonFungibleESDTv2), }, { Path: directStakedInfoPath, diff --git a/api/groups/networkGroup_test.go b/api/groups/networkGroup_test.go index 3eb52a4a0c0..c809a632b56 100644 --- a/api/groups/networkGroup_test.go +++ b/api/groups/networkGroup_test.go @@ -206,6 +206,36 @@ func TestNetworkConfigMetrics_GasLimitGuardedTxShouldWork(t *testing.T) { assert.True(t, keyAndValueFoundInResponse) } +func TestNetworkConfigMetrics_GasLimitRelayedTxShouldWork(t *testing.T) { + t.Parallel() + + statusMetricsProvider := statusHandler.NewStatusMetrics() + key := common.MetricExtraGasLimitRelayedTx + val := uint64(123) + statusMetricsProvider.SetUInt64Value(key, val) + + facade := mock.FacadeStub{} + facade.StatusMetricsHandler = func() external.StatusMetricsHandler { + return statusMetricsProvider + } + + networkGroup, err := groups.NewNetworkGroup(&facade) + require.NoError(t, err) + + ws := startWebServer(networkGroup, "network", getNetworkRoutesConfig()) + + req, _ := http.NewRequest("GET", "/network/config", nil) + resp := httptest.NewRecorder() + ws.ServeHTTP(resp, req) + + respBytes, _ := io.ReadAll(resp.Body) + respStr := string(respBytes) + assert.Equal(t, resp.Code, http.StatusOK) + + keyAndValueFoundInResponse := strings.Contains(respStr, key) && strings.Contains(respStr, fmt.Sprintf("%d", val)) + assert.True(t, keyAndValueFoundInResponse) +} + func TestNetworkStatusMetrics_ShouldWork(t *testing.T) { t.Parallel() 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/config.toml b/cmd/node/config/config.toml index 6ccb3732763..20c1c52e7af 100644 --- a/cmd/node/config/config.toml +++ b/cmd/node/config/config.toml @@ -386,9 +386,9 @@ [TxDataPool] Name = "TxDataPool" Capacity = 600000 - SizePerSender = 20000 + SizePerSender = 5001 SizeInBytes = 419430400 #400MB - SizeInBytesPerSender = 12288000 + SizeInBytesPerSender = 12288000 #12MB Type = "TxCache" Shards = 16 diff --git a/cmd/node/config/enableEpochs.toml b/cmd/node/config/enableEpochs.toml index 05f87927b90..ccce6d760f7 100644 --- a/cmd/node/config/enableEpochs.toml +++ b/cmd/node/config/enableEpochs.toml @@ -313,25 +313,28 @@ UseGasBoundedShouldFailExecutionEnableEpoch = 1 # DynamicESDTEnableEpoch represents the epoch when dynamic NFT feature is enabled - DynamicESDTEnableEpoch = 4 + DynamicESDTEnableEpoch = 1 # EGLDInMultiTransferEnableEpoch represents the epoch when EGLD in multitransfer is enabled - EGLDInMultiTransferEnableEpoch = 4 + EGLDInMultiTransferEnableEpoch = 1 # CryptoOpcodesV2EnableEpoch represents the epoch when BLSMultiSig, Secp256r1 and other opcodes are enabled - CryptoOpcodesV2EnableEpoch = 4 + CryptoOpcodesV2EnableEpoch = 1 # UnjailCleanupEnableEpoch represents the epoch when the cleanup of the unjailed nodes is enabled - UnJailCleanupEnableEpoch = 4 + UnJailCleanupEnableEpoch = 1 # 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 # FixRelayedMoveBalanceToNonPayableSCEnableEpoch represents the epoch when the fix for relayed move balance to non payable sc will be enabled - FixRelayedMoveBalanceToNonPayableSCEnableEpoch = 4 + FixRelayedMoveBalanceToNonPayableSCEnableEpoch = 1 + + # RelayedTransactionsV3EnableEpoch represents the epoch when the relayed transactions v3 will be enabled + RelayedTransactionsV3EnableEpoch = 2 # ConsensusModelV2EnableEpoch represents the epoch when the consensus model V2 is enabled ConsensusModelV2EnableEpoch = 2 diff --git a/cmd/node/main.go b/cmd/node/main.go index c7cc3c1085c..5a812bc2f45 100644 --- a/cmd/node/main.go +++ b/cmd/node/main.go @@ -290,6 +290,7 @@ func attachFileLogger(log logger.Logger, flagsConfig *config.ContextFlagsConfig) logger.ToggleCorrelation(flagsConfig.EnableLogCorrelation) logger.ToggleLoggerName(flagsConfig.EnableLogName) logLevelFlagValue := flagsConfig.LogLevel + err = logger.SetLogLevel(logLevelFlagValue) if err != nil { return nil, err diff --git a/cmd/sovereignnode/config/enableEpochs.toml b/cmd/sovereignnode/config/enableEpochs.toml index ea0836b4741..4ea439cabc2 100644 --- a/cmd/sovereignnode/config/enableEpochs.toml +++ b/cmd/sovereignnode/config/enableEpochs.toml @@ -90,9 +90,6 @@ # ValidatorToDelegationEnableEpoch represents the epoch when the validator-to-delegation feature will be enabled ValidatorToDelegationEnableEpoch = 0 - # WaitingListFixEnableEpoch represents the epoch when the 6 epoch waiting list fix is enabled - WaitingListFixEnableEpoch = 0 - # IncrementSCRNonceInMultiTransferEnableEpoch represents the epoch when the fix for preventing the generation of the same SCRs # is enabled. The fix is done by adding an extra increment. IncrementSCRNonceInMultiTransferEnableEpoch = 0 @@ -239,6 +236,109 @@ # RuntimeCodeSizeFixEnableEpoch represents the epoch when the code size fix in the VM is enabled RuntimeCodeSizeFixEnableEpoch = 0 + # RelayedNonceFixEnableEpoch represents the epoch when the nonce fix for relayed txs is enabled + RelayedNonceFixEnableEpoch = 0 + + # SetGuardianEnableEpoch represents the epoch when the guard account feature is enabled in the protocol + SetGuardianEnableEpoch = 0 + + # DeterministicSortOnValidatorsInfoEnableEpoch represents the epoch when the deterministic sorting on validators info is enabled + DeterministicSortOnValidatorsInfoEnableEpoch = 0 + + # SCProcessorV2EnableEpoch represents the epoch when SC processor V2 will be used + SCProcessorV2EnableEpoch = 0 + + # AutoBalanceDataTriesEnableEpoch represents the epoch when the data tries are automatically balanced by inserting at the hashed key instead of the normal key + AutoBalanceDataTriesEnableEpoch = 0 + + # MigrateDataTrieEnableEpoch represents the epoch when the data tries migration is enabled + MigrateDataTrieEnableEpoch = 0 + + # KeepExecOrderOnCreatedSCRsEnableEpoch represents the epoch when the execution order of created SCRs is ensured + KeepExecOrderOnCreatedSCRsEnableEpoch = 0 + + # MultiClaimOnDelegationEnableEpoch represents the epoch when the multi claim on delegation is enabled + MultiClaimOnDelegationEnableEpoch = 0 + + # ChangeUsernameEnableEpoch represents the epoch when changing username is enabled + ChangeUsernameEnableEpoch = 0 + + # ConsistentTokensValuesLengthCheckEnableEpoch represents the epoch when the consistent tokens values length check is enabled + ConsistentTokensValuesLengthCheckEnableEpoch = 0 + + # FixDelegationChangeOwnerOnAccountEnableEpoch represents the epoch when the fix for the delegation system smart contract is enabled + FixDelegationChangeOwnerOnAccountEnableEpoch = 0 + + # DynamicGasCostForDataTrieStorageLoadEnableEpoch represents the epoch when dynamic gas cost for data trie storage load will be enabled + DynamicGasCostForDataTrieStorageLoadEnableEpoch = 0 + + # ScToScLogEventEnableEpoch represents the epoch when the sc to sc log event feature is enabled + ScToScLogEventEnableEpoch = 0 + + # NFTStopCreateEnableEpoch represents the epoch when NFT stop create feature is enabled + NFTStopCreateEnableEpoch = 0 + + # ChangeOwnerAddressCrossShardThroughSCEnableEpoch represents the epoch when the change owner address built in function will work also through a smart contract call cross shard + ChangeOwnerAddressCrossShardThroughSCEnableEpoch = 0 + + # FixGasRemainingForSaveKeyValueBuiltinFunctionEnableEpoch represents the epoch when the fix for the remaining gas in the SaveKeyValue builtin function is enabled + FixGasRemainingForSaveKeyValueBuiltinFunctionEnableEpoch = 0 + + # CurrentRandomnessOnSortingEnableEpoch represents the epoch when the current randomness on sorting is enabled + CurrentRandomnessOnSortingEnableEpoch = 0 + + # StakeLimitsEnableEpoch represents the epoch when stake limits on validators are enabled + # Should have the same value as StakingV4Step1EnableEpoch that triggers the automatic unstake operations for the queue nodes + StakeLimitsEnableEpoch = 0 + + # StakingV4Step1EnableEpoch represents the epoch when staking v4 is initialized. This is the epoch in which + # all nodes from staking queue are moved in the auction list + StakingV4Step1EnableEpoch = 0 + + # StakingV4Step2EnableEpoch represents the epoch when staking v4 is enabled. Should have a greater value than StakingV4Step1EnableEpoch. + # From this epoch, all shuffled out nodes are moved to auction nodes. No auction nodes selection is done yet. + StakingV4Step2EnableEpoch = 0 + + # StakingV4Step3EnableEpoch represents the epoch in which selected nodes from auction will be distributed to waiting list + StakingV4Step3EnableEpoch = 0 + + # CleanupAuctionOnLowWaitingListEnableEpoch represents the epoch when duplicated data cleanup from auction list is enabled in the condition of a low waiting list + # Should have the same value as StakingV4Step1EnableEpoch if the low waiting list has not happened, otherwise should have a greater value + CleanupAuctionOnLowWaitingListEnableEpoch = 0 + + # AlwaysMergeContextsInEEIEnableEpoch represents the epoch in which the EEI will always merge the contexts + AlwaysMergeContextsInEEIEnableEpoch = 0 + + # UseGasBoundedShouldFailExecutionEnableEpoch represents the epoch when use bounded gas function should fail execution in case of error + UseGasBoundedShouldFailExecutionEnableEpoch = 0 + + # DynamicESDTEnableEpoch represents the epoch when dynamic NFT feature is enabled + DynamicESDTEnableEpoch = 0 + + # EGLDInMultiTransferEnableEpoch represents the epoch when EGLD in multitransfer is enabled + EGLDInMultiTransferEnableEpoch = 0 + + # CryptoOpcodesV2EnableEpoch represents the epoch when BLSMultiSig, Secp256r1 and other opcodes are enabled + CryptoOpcodesV2EnableEpoch = 0 + + # UnjailCleanupEnableEpoch represents the epoch when the cleanup of the unjailed nodes is enabled + UnJailCleanupEnableEpoch = 0 + + # FixRelayedBaseCostEnableEpoch represents the epoch when the fix for relayed base cost will be enabled + FixRelayedBaseCostEnableEpoch = 0 + + # MultiESDTNFTTransferAndExecuteByUserEnableEpoch represents the epoch when enshrined sovereign cross chain opcodes are enabled + MultiESDTNFTTransferAndExecuteByUserEnableEpoch = 9999999 + + # FixRelayedMoveBalanceToNonPayableSCEnableEpoch represents the epoch when the fix for relayed move balance to non payable sc will be enabled + FixRelayedMoveBalanceToNonPayableSCEnableEpoch = 0 + + # RelayedTransactionsV3EnableEpoch represents the epoch when the relayed transactions v3 will be enabled + RelayedTransactionsV3EnableEpoch = 0 + + # ConsensusModelV2EnableEpoch represents the epoch when the consensus model V2 is enabled + ConsensusModelV2EnableEpoch = 0 + # BLSMultiSignerEnableEpoch represents the activation epoch for different types of BLS multi-signers BLSMultiSignerEnableEpoch = [ { EnableEpoch = 0, Type = "KOSK"} diff --git a/cmd/sovereignnode/go.mod b/cmd/sovereignnode/go.mod index 3589dd31294..58feefb6aa7 100644 --- a/cmd/sovereignnode/go.mod +++ b/cmd/sovereignnode/go.mod @@ -6,7 +6,7 @@ go 1.20 require ( github.com/google/gops v0.3.18 - github.com/multiversx/mx-chain-core-go v1.2.24-0.20241209142414-762196f8dd06 + github.com/multiversx/mx-chain-core-go v1.2.25-0.20250131122047-725dd68e256f github.com/multiversx/mx-chain-go v1.8.4 github.com/multiversx/mx-chain-logger-go v1.0.15 github.com/multiversx/mx-chain-sovereign-bridge-go v0.0.0-20250128122723-700d9ae97f96 @@ -120,9 +120,9 @@ require ( github.com/multiversx/concurrent-map v0.1.4 // indirect github.com/multiversx/mx-chain-communication-go v1.1.1 // indirect github.com/multiversx/mx-chain-crypto-go v1.2.12 // indirect - github.com/multiversx/mx-chain-es-indexer-go v1.7.11-0.20250128112625-86bc0754b1e9 // indirect + github.com/multiversx/mx-chain-es-indexer-go v1.7.15-0.20250131125841-73868f5ca49c // indirect github.com/multiversx/mx-chain-scenario-go v1.4.4 // indirect - github.com/multiversx/mx-chain-storage-go v1.0.16 // indirect + github.com/multiversx/mx-chain-storage-go v1.0.19 // indirect github.com/multiversx/mx-chain-vm-common-go v1.5.17-0.20241119132002-2fa80c5ec516 // indirect github.com/multiversx/mx-chain-vm-go v1.5.37 // indirect github.com/multiversx/mx-chain-vm-v1_2-go v1.2.68 // indirect diff --git a/cmd/sovereignnode/go.sum b/cmd/sovereignnode/go.sum index d7770d2714b..a89b8e84feb 100644 --- a/cmd/sovereignnode/go.sum +++ b/cmd/sovereignnode/go.sum @@ -385,12 +385,12 @@ 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.20241209142414-762196f8dd06 h1:3Sog/pzWhtQ7UKHTaJhK50EjSqlkxTOD4JKWmJZzr50= -github.com/multiversx/mx-chain-core-go v1.2.24-0.20241209142414-762196f8dd06/go.mod h1:P/YBoFnt25XUaCQ7Q/SD15vhnc9yV5JDhHxyFO9P8Z0= +github.com/multiversx/mx-chain-core-go v1.2.25-0.20250131122047-725dd68e256f h1:iyDk0Zk0n4pHZnrdX30SxevTy+xg9GcEyAmBss45Nfs= +github.com/multiversx/mx-chain-core-go v1.2.25-0.20250131122047-725dd68e256f/go.mod h1:P/YBoFnt25XUaCQ7Q/SD15vhnc9yV5JDhHxyFO9P8Z0= 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.20250128112625-86bc0754b1e9 h1:rQL4GS2fLvSHzPqbZ08emtfh+IzMkFJOlwtNenyHtAI= -github.com/multiversx/mx-chain-es-indexer-go v1.7.11-0.20250128112625-86bc0754b1e9/go.mod h1:i5xoCUsrHteffxx3yfR8bj9tmpP/DtastZGOc/KRAoY= +github.com/multiversx/mx-chain-es-indexer-go v1.7.15-0.20250131125841-73868f5ca49c h1:Qm84hhybDLTeS+BLebFYgmGJpkX+AUnzZsUC+u9KzBU= +github.com/multiversx/mx-chain-es-indexer-go v1.7.15-0.20250131125841-73868f5ca49c/go.mod h1:u3P9JM8JWPRcYW3X0Lsw06Nf6a6M7cS42XwT4Au/Ar8= 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= @@ -399,8 +399,8 @@ github.com/multiversx/mx-chain-sovereign-bridge-go v0.0.0-20250128122723-700d9ae github.com/multiversx/mx-chain-sovereign-bridge-go v0.0.0-20250128122723-700d9ae97f96/go.mod h1:K907bMZUkW/JLKaHkkQbEJMqyMoy1xwBBUW7XHXp4hg= github.com/multiversx/mx-chain-sovereign-notifier-go v0.0.0-20230929085947-df9b345f49ac h1:GtFxKINPiDCsqjKpTWHFN/5qvQGnFClYH4jMHNrJx/M= github.com/multiversx/mx-chain-sovereign-notifier-go v0.0.0-20230929085947-df9b345f49ac/go.mod h1:syNNd30uEkKsz2V5nXCfv3u+KhkpKVw34+2DsfSuFSE= -github.com/multiversx/mx-chain-storage-go v1.0.16 h1:l2lJq+EAN3YwLbjJrnoKfFd1/1Xmo9DcAUECND2obLs= -github.com/multiversx/mx-chain-storage-go v1.0.16/go.mod h1:uM/z7YyqTOD3wgyH8TfapyEl5sb+7x/Jaxne4cfG4HI= +github.com/multiversx/mx-chain-storage-go v1.0.19 h1:2R35MoSXcuNJOFmV5xEhcXqiEGZw6AYGy9R8J9KH66Q= +github.com/multiversx/mx-chain-storage-go v1.0.19/go.mod h1:Pb/BuVmiFqO66DSZO16KFkSUeom94x3e3Q9IloBvkYI= github.com/multiversx/mx-chain-vm-common-go v1.5.17-0.20241119132002-2fa80c5ec516 h1:PToP1B7aFkXjQBOF5MDCCvsc+ffWVqqBHtmemfCwVA8= github.com/multiversx/mx-chain-vm-common-go v1.5.17-0.20241119132002-2fa80c5ec516/go.mod h1:C7KVj6/+TAhxDjgY7oAMO5wSj7WbBYIJ5TCMzmxk2w0= github.com/multiversx/mx-chain-vm-go v1.5.37 h1:Iy3KCvM+DOq1f9UPA7uYK/rI3ZbBOXc2CVNO2/vm5zw= diff --git a/cmd/sovereignnode/sovereignNodeRunner.go b/cmd/sovereignnode/sovereignNodeRunner.go index e3e0d26f7c9..2dd14ea4b77 100644 --- a/cmd/sovereignnode/sovereignNodeRunner.go +++ b/cmd/sovereignnode/sovereignNodeRunner.go @@ -1227,6 +1227,7 @@ func (snr *sovereignNodeRunner) CreateManagedStatusComponents( StatusCoreComponents: managedStatusCoreComponents, CryptoComponents: cryptoComponents, IsSovereign: true, + ESDTPrefix: snr.configs.SystemSCConfig.ESDTSystemSCConfig.ESDTPrefix, } statusComponentsFactory, err := statusComp.NewStatusComponentsFactory(statArgs) diff --git a/common/common.go b/common/common.go new file mode 100644 index 00000000000..c1e565043ad --- /dev/null +++ b/common/common.go @@ -0,0 +1,26 @@ +package common + +import "github.com/multiversx/mx-chain-core-go/data" + +// 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 { + 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 +} + +// 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 new file mode 100644 index 00000000000..5a0ec53a21f --- /dev/null +++ b/common/common_test.go @@ -0,0 +1,70 @@ +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 TestIsValidRelayedTxV3(t *testing.T) { + t.Parallel() + + scr := &smartContractResult.SmartContractResult{} + require.False(t, IsValidRelayedTxV3(scr)) + 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, 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, + 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, IsValidRelayedTxV3(relayedTxV3)) + require.True(t, IsRelayedTxV3(relayedTxV3)) +} diff --git a/common/constants.go b/common/constants.go index c32f6f095aa..c877458320c 100644 --- a/common/constants.go +++ b/common/constants.go @@ -340,6 +340,9 @@ const MetricMinGasLimit = "erd_min_gas_limit" // MetricExtraGasLimitGuardedTx specifies the extra gas limit required for guarded transactions const MetricExtraGasLimitGuardedTx = "erd_extra_gas_limit_guarded_tx" +// MetricExtraGasLimitRelayedTx specifies the extra gas limit required for relayed v3 transactions +const MetricExtraGasLimitRelayedTx = "erd_extra_gas_limit_relayed_tx" + // MetricRewardsTopUpGradientPoint is the metric that specifies the rewards top up gradient point const MetricRewardsTopUpGradientPoint = "erd_rewards_top_up_gradient_point" @@ -737,6 +740,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,6 +1240,7 @@ const ( FixRelayedBaseCostFlag core.EnableEpochFlag = "FixRelayedBaseCostFlag" MultiESDTNFTTransferAndExecuteByUserFlag core.EnableEpochFlag = "MultiESDTNFTTransferAndExecuteByUserFlag" FixRelayedMoveBalanceToNonPayableSCFlag core.EnableEpochFlag = "FixRelayedMoveBalanceToNonPayableSCFlag" + RelayedTransactionsV3Flag core.EnableEpochFlag = "RelayedTransactionsV3Flag" ConsensusModelV2Flag core.EnableEpochFlag = "ConsensusModelV2Flag" // 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 e412aa5408f..9d30f6757f5 100644 --- a/common/enablers/enableEpochsHandler.go +++ b/common/enablers/enableEpochsHandler.go @@ -784,6 +784,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, + }, common.ConsensusModelV2Flag: { isActiveInEpoch: func(epoch uint32) bool { return epoch >= handler.enableEpochsConfig.ConsensusModelV2EnableEpoch diff --git a/common/enablers/enableEpochsHandler_test.go b/common/enablers/enableEpochsHandler_test.go index 02d45a20ad1..a642c7e310a 100644 --- a/common/enablers/enableEpochsHandler_test.go +++ b/common/enablers/enableEpochsHandler_test.go @@ -124,6 +124,7 @@ func createEnableEpochsConfig() config.EnableEpochs { MultiESDTNFTTransferAndExecuteByUserEnableEpoch: 106, FixRelayedMoveBalanceToNonPayableSCEnableEpoch: 107, UseGasBoundedShouldFailExecutionEnableEpoch: 108, + RelayedTransactionsV3EnableEpoch: 109, } } @@ -450,6 +451,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)) require.Equal(t, cfg.ConsensusModelV2EnableEpoch, handler.GetActivationEpoch(common.ConsensusModelV2Flag)) } diff --git a/common/reflectcommon/structFieldsUpdate.go b/common/reflectcommon/structFieldsUpdate.go index be8671eff4f..7f8940b0400 100644 --- a/common/reflectcommon/structFieldsUpdate.go +++ b/common/reflectcommon/structFieldsUpdate.go @@ -126,7 +126,7 @@ func trySetTheNewValue(value *reflect.Value, newValue interface{}) error { 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 := trySetStructValue(&newItem, item) + err := trySetTheNewValue(&newItem, item.Interface()) if err != nil { return err } @@ -167,7 +167,7 @@ func trySetStructValue(value *reflect.Value, newValue reflect.Value) error { } } -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())) } diff --git a/common/reflectcommon/structFieldsUpdate_test.go b/common/reflectcommon/structFieldsUpdate_test.go index 44d3ae7d694..27a30ea9d00 100644 --- a/common/reflectcommon/structFieldsUpdate_test.go +++ b/common/reflectcommon/structFieldsUpdate_test.go @@ -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", 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", 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 from toml", 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 from toml", 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 arrays from toml", 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/config/epochConfig.go b/config/epochConfig.go index 0fc78e95853..83d18644933 100644 --- a/config/epochConfig.go +++ b/config/epochConfig.go @@ -122,6 +122,7 @@ type EnableEpochs struct { FixRelayedBaseCostEnableEpoch uint32 MultiESDTNFTTransferAndExecuteByUserEnableEpoch uint32 FixRelayedMoveBalanceToNonPayableSCEnableEpoch uint32 + RelayedTransactionsV3EnableEpoch uint32 ConsensusModelV2EnableEpoch uint32 BLSMultiSignerEnableEpoch []MultiSignerConfig } diff --git a/config/tomlConfig_test.go b/config/tomlConfig_test.go index d5a29ea284b..bbe78ee032e 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 + # ConsensusModelV2EnableEpoch represents the epoch when the consensus model V2 is enabled ConsensusModelV2EnableEpoch = 69 @@ -1006,6 +1009,7 @@ func TestEnableEpochConfig(t *testing.T) { FixRelayedBaseCostEnableEpoch: 100, MultiESDTNFTTransferAndExecuteByUserEnableEpoch: 101, FixRelayedMoveBalanceToNonPayableSCEnableEpoch: 102, + RelayedTransactionsV3EnableEpoch: 103, MaxNodesChangeEnableEpoch: []MaxNodesChangeConfig{ { EpochEnable: 44, diff --git a/dataRetriever/constants.go b/dataRetriever/constants.go index ef8ceaca082..198e2ea4ea0 100644 --- a/dataRetriever/constants.go +++ b/dataRetriever/constants.go @@ -1,8 +1,5 @@ package dataRetriever -// TxPoolNumSendersToPreemptivelyEvict instructs tx pool eviction algorithm to remove this many senders when eviction takes place -const TxPoolNumSendersToPreemptivelyEvict = uint32(100) - // UnsignedTxPoolName defines the name of the unsigned transactions pool const UnsignedTxPoolName = "uTxPool" diff --git a/dataRetriever/errors.go b/dataRetriever/errors.go index a015e6e10ed..8b7b2f2e3dc 100644 --- a/dataRetriever/errors.go +++ b/dataRetriever/errors.go @@ -116,9 +116,6 @@ var ErrCacheConfigInvalidSize = errors.New("cache parameter [size] is not valid, // ErrCacheConfigInvalidShards signals that the cache parameter "shards" is invalid var ErrCacheConfigInvalidShards = errors.New("cache parameter [shards] is not valid, it must be a positive number") -// ErrCacheConfigInvalidEconomics signals that an economics parameter required by the cache is invalid -var ErrCacheConfigInvalidEconomics = errors.New("cache-economics parameter is not valid") - // ErrCacheConfigInvalidSharding signals that a sharding parameter required by the cache is invalid var ErrCacheConfigInvalidSharding = errors.New("cache-sharding parameter is not valid") diff --git a/dataRetriever/factory/dataPoolFactory.go b/dataRetriever/factory/dataPoolFactory.go index 6e1415ddfd8..f48dc3d3c37 100644 --- a/dataRetriever/factory/dataPoolFactory.go +++ b/dataRetriever/factory/dataPoolFactory.go @@ -53,6 +53,9 @@ func NewDataPoolFromConfig(args ArgsDataPool) (dataRetriever.PoolsHolder, error) if check.IfNil(args.ShardCoordinator) { return nil, dataRetriever.ErrNilShardCoordinator } + if check.IfNil(args.Marshalizer) { + return nil, dataRetriever.ErrNilMarshalizer + } if check.IfNil(args.PathManager) { return nil, dataRetriever.ErrNilPathManager } @@ -61,9 +64,10 @@ func NewDataPoolFromConfig(args ArgsDataPool) (dataRetriever.PoolsHolder, error) txPool, err := txpool.NewShardedTxPool(txpool.ArgShardedTxPool{ Config: factory.GetCacherFromConfig(mainConfig.TxDataPool), + TxGasHandler: args.EconomicsData, + Marshalizer: args.Marshalizer, NumberOfShards: args.ShardCoordinator.NumberOfShards(), SelfShardID: args.ShardCoordinator.SelfId(), - TxGasHandler: args.EconomicsData, }) if err != nil { return nil, fmt.Errorf("%w while creating the cache for the transactions", err) diff --git a/dataRetriever/factory/dataPoolFactory_test.go b/dataRetriever/factory/dataPoolFactory_test.go index c9ae8b60c43..b4896244ad9 100644 --- a/dataRetriever/factory/dataPoolFactory_test.go +++ b/dataRetriever/factory/dataPoolFactory_test.go @@ -41,6 +41,12 @@ func TestNewDataPoolFromConfig_MissingDependencyShouldErr(t *testing.T) { require.Nil(t, holder) require.Equal(t, dataRetriever.ErrNilShardCoordinator, err) + args = getGoodArgs() + args.Marshalizer = nil + holder, err = NewDataPoolFromConfig(args) + require.Nil(t, holder) + require.Equal(t, dataRetriever.ErrNilMarshalizer, err) + args = getGoodArgs() args.PathManager = nil holder, err = NewDataPoolFromConfig(args) @@ -76,7 +82,6 @@ func TestNewDataPoolFromConfig_BadConfigShouldErr(t *testing.T) { args.Config.HeadersPoolConfig.MaxHeadersPerShard = 0 holder, err = NewDataPoolFromConfig(args) require.Nil(t, holder) - fmt.Println(err) require.True(t, errors.Is(err, headersCache.ErrInvalidHeadersCacheParameter)) require.True(t, strings.Contains(err.Error(), "the cache for the headers")) @@ -84,7 +89,6 @@ func TestNewDataPoolFromConfig_BadConfigShouldErr(t *testing.T) { args.Config.TxBlockBodyDataPool.Capacity = 0 holder, err = NewDataPoolFromConfig(args) require.Nil(t, holder) - fmt.Println(err) require.NotNil(t, err) require.True(t, strings.Contains(err.Error(), "must provide a positive size while creating the cache for the miniblocks")) @@ -92,7 +96,6 @@ func TestNewDataPoolFromConfig_BadConfigShouldErr(t *testing.T) { args.Config.PeerBlockBodyDataPool.Capacity = 0 holder, err = NewDataPoolFromConfig(args) require.Nil(t, holder) - fmt.Println(err) require.NotNil(t, err) require.True(t, strings.Contains(err.Error(), "must provide a positive size while creating the cache for the peer mini block body")) @@ -100,19 +103,9 @@ func TestNewDataPoolFromConfig_BadConfigShouldErr(t *testing.T) { args.Config.TrieSyncStorage.Capacity = 0 holder, err = NewDataPoolFromConfig(args) require.Nil(t, holder) - fmt.Println(err) require.True(t, errors.Is(err, storage.ErrCacheSizeInvalid)) require.True(t, strings.Contains(err.Error(), "the cache for the trie nodes")) - args = getGoodArgs() - args.Config.TrieSyncStorage.EnableDB = true - args.Config.TrieSyncStorage.DB.Type = "invalid DB type" - holder, err = NewDataPoolFromConfig(args) - require.Nil(t, holder) - fmt.Println(err) - require.True(t, errors.Is(err, storage.ErrNotSupportedDBType)) - require.True(t, strings.Contains(err.Error(), "the db for the trie nodes")) - args = getGoodArgs() args.Config.TrieNodesChunksDataPool.Type = "invalid cache type" holder, err = NewDataPoolFromConfig(args) diff --git a/dataRetriever/requestHandlers/requestHandler.go b/dataRetriever/requestHandlers/requestHandler.go index 6fd1ced8a68..9cff953e8f8 100644 --- a/dataRetriever/requestHandlers/requestHandler.go +++ b/dataRetriever/requestHandlers/requestHandler.go @@ -10,7 +10,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/core/partitioning" - "github.com/multiversx/mx-chain-logger-go" + logger "github.com/multiversx/mx-chain-logger-go" "github.com/multiversx/mx-chain-go/common" "github.com/multiversx/mx-chain-go/dataRetriever" @@ -569,10 +569,12 @@ func (rrh *resolverRequestHandler) RequestValidatorInfo(hash []byte) { return } + epoch := rrh.getEpoch() + log.Debug("requesting validator info messages from network", "topic", common.ValidatorInfoTopic, "hash", hash, - "epoch", rrh.epoch, + "epoch", epoch, ) requester, err := rrh.baseRequestHandler.getValidatorsInfoRequester() @@ -581,20 +583,20 @@ func (rrh *resolverRequestHandler) RequestValidatorInfo(hash []byte) { "error", err.Error(), "topic", common.ValidatorInfoTopic, "hash", hash, - "epoch", rrh.epoch, + "epoch", epoch, ) return } rrh.whiteList.Add([][]byte{hash}) - err = requester.RequestDataFromHash(hash, rrh.epoch) + err = requester.RequestDataFromHash(hash, epoch) if err != nil { log.Debug("RequestValidatorInfo.RequestDataFromHash", "error", err.Error(), "topic", common.ValidatorInfoTopic, "hash", hash, - "epoch", rrh.epoch, + "epoch", epoch, ) return } @@ -609,10 +611,12 @@ func (rrh *resolverRequestHandler) RequestValidatorsInfo(hashes [][]byte) { return } + epoch := rrh.getEpoch() + log.Debug("requesting validator info messages from network", "topic", common.ValidatorInfoTopic, "num hashes", len(unrequestedHashes), - "epoch", rrh.epoch, + "epoch", epoch, ) requester, err := rrh.baseRequestHandler.getValidatorsInfoRequester() @@ -621,7 +625,7 @@ func (rrh *resolverRequestHandler) RequestValidatorsInfo(hashes [][]byte) { "error", err.Error(), "topic", common.ValidatorInfoTopic, "num hashes", len(unrequestedHashes), - "epoch", rrh.epoch, + "epoch", epoch, ) return } @@ -634,13 +638,13 @@ func (rrh *resolverRequestHandler) RequestValidatorsInfo(hashes [][]byte) { rrh.whiteList.Add(unrequestedHashes) - err = validatorInfoRequester.RequestDataFromHashArray(unrequestedHashes, rrh.epoch) + err = validatorInfoRequester.RequestDataFromHashArray(unrequestedHashes, epoch) if err != nil { log.Debug("RequestValidatorInfo.RequestDataFromHash", "error", err.Error(), "topic", common.ValidatorInfoTopic, "num hashes", len(unrequestedHashes), - "epoch", rrh.epoch, + "epoch", epoch, ) return } @@ -777,11 +781,13 @@ func (rrh *resolverRequestHandler) GetNumPeersToQuery(key string) (int, int, err // RequestPeerAuthenticationsByHashes asks for peer authentication messages from specific peers hashes func (rrh *resolverRequestHandler) RequestPeerAuthenticationsByHashes(destShardID uint32, hashes [][]byte) { + epoch := rrh.getEpoch() + log.Debug("requesting peer authentication messages from network", "topic", common.PeerAuthenticationTopic, "shard", destShardID, "num hashes", len(hashes), - "epoch", rrh.epoch, + "epoch", epoch, ) requester, err := rrh.requestersFinder.MetaChainRequester(common.PeerAuthenticationTopic) @@ -790,7 +796,7 @@ func (rrh *resolverRequestHandler) RequestPeerAuthenticationsByHashes(destShardI "error", err.Error(), "topic", common.PeerAuthenticationTopic, "shard", destShardID, - "epoch", rrh.epoch, + "epoch", epoch, ) return } @@ -801,13 +807,13 @@ func (rrh *resolverRequestHandler) RequestPeerAuthenticationsByHashes(destShardI return } - err = peerAuthRequester.RequestDataFromHashArray(hashes, rrh.epoch) + err = peerAuthRequester.RequestDataFromHashArray(hashes, epoch) if err != nil { log.Debug("RequestPeerAuthenticationsByHashes.RequestDataFromHashArray", "error", err.Error(), "topic", common.PeerAuthenticationTopic, "shard", destShardID, - "epoch", rrh.epoch, + "epoch", epoch, ) } } diff --git a/dataRetriever/shardedData/shardedData.go b/dataRetriever/shardedData/shardedData.go index 2800bb3352b..785998164b9 100644 --- a/dataRetriever/shardedData/shardedData.go +++ b/dataRetriever/shardedData/shardedData.go @@ -4,6 +4,7 @@ import ( "fmt" "sync" + "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-core-go/core/counting" "github.com/multiversx/mx-chain-core-go/marshal" "github.com/multiversx/mx-chain-go/dataRetriever" @@ -52,7 +53,7 @@ func NewShardedData(name string, config storageunit.CacheConfig) (*shardedData, NumChunks: config.Shards, MaxNumItems: config.Capacity, MaxNumBytes: uint32(config.SizeInBytes), - NumItemsToPreemptivelyEvict: storage.TxPoolNumTxsToPreemptivelyEvict, + NumItemsToPreemptivelyEvict: storage.ShardedDataNumItemsToPreemptivelyEvict, } err := configPrototype.Verify() @@ -161,6 +162,9 @@ func (sd *shardedData) RemoveSetOfDataFromPool(keys [][]byte, cacheID string) { return } + stopWatch := core.NewStopWatch() + stopWatch.Start("removal") + numRemoved := 0 for _, key := range keys { if store.cache.RemoveWithResult(key) { @@ -168,7 +172,15 @@ func (sd *shardedData) RemoveSetOfDataFromPool(keys [][]byte, cacheID string) { } } - log.Trace("shardedData.removeTxBulk()", "name", sd.name, "cacheID", cacheID, "numToRemove", len(keys), "numRemoved", numRemoved) + stopWatch.Stop("removal") + + log.Debug("shardedData.removeTxBulk", + "name", sd.name, + "cacheID", cacheID, + "numToRemove", len(keys), + "numRemoved", numRemoved, + "duration", stopWatch.GetMeasurement("removal"), + ) } // ImmunizeSetOfDataAgainstEviction marks the items as non-evictable diff --git a/dataRetriever/txpool/argShardedTxPool.go b/dataRetriever/txpool/argShardedTxPool.go index 8b12dbddf7a..dca1efa56bd 100644 --- a/dataRetriever/txpool/argShardedTxPool.go +++ b/dataRetriever/txpool/argShardedTxPool.go @@ -1,19 +1,19 @@ package txpool import ( - "encoding/json" "fmt" "github.com/multiversx/mx-chain-core-go/core/check" + "github.com/multiversx/mx-chain-core-go/marshal" "github.com/multiversx/mx-chain-go/dataRetriever" "github.com/multiversx/mx-chain-go/storage/storageunit" - "github.com/multiversx/mx-chain-go/storage/txcache" ) // ArgShardedTxPool is the argument for ShardedTxPool's constructor type ArgShardedTxPool struct { Config storageunit.CacheConfig - TxGasHandler txcache.TxGasHandler + TxGasHandler txGasHandler + Marshalizer marshal.Marshalizer NumberOfShards uint32 SelfShardID uint32 } @@ -40,8 +40,8 @@ func (args *ArgShardedTxPool) verify() error { if check.IfNil(args.TxGasHandler) { return fmt.Errorf("%w: TxGasHandler is not valid", dataRetriever.ErrNilTxGasHandler) } - if args.TxGasHandler.MinGasPrice() == 0 { - return fmt.Errorf("%w: MinGasPrice is not valid", dataRetriever.ErrCacheConfigInvalidEconomics) + if check.IfNil(args.Marshalizer) { + return fmt.Errorf("%w: Marshalizer is not valid", dataRetriever.ErrNilMarshalizer) } if args.NumberOfShards == 0 { return fmt.Errorf("%w: NumberOfShards is not valid", dataRetriever.ErrCacheConfigInvalidSharding) @@ -49,13 +49,3 @@ func (args *ArgShardedTxPool) verify() error { return nil } - -// String returns a readable representation of the object -func (args *ArgShardedTxPool) String() string { - bytes, err := json.Marshal(args) - if err != nil { - log.Error("ArgShardedTxPool.String()", "err", err) - } - - return string(bytes) -} diff --git a/dataRetriever/txpool/interface.go b/dataRetriever/txpool/interface.go index 6579659d692..ee55a246a48 100644 --- a/dataRetriever/txpool/interface.go +++ b/dataRetriever/txpool/interface.go @@ -1,6 +1,9 @@ package txpool import ( + "math/big" + + "github.com/multiversx/mx-chain-core-go/data" "github.com/multiversx/mx-chain-go/storage" "github.com/multiversx/mx-chain-go/storage/txcache" ) @@ -17,3 +20,8 @@ type txCache interface { Diagnose(deep bool) GetTransactionsPoolForSender(sender string) []*txcache.WrappedTransaction } + +type txGasHandler interface { + ComputeTxFee(tx data.TransactionWithFeeHandler) *big.Int + IsInterfaceNil() bool +} diff --git a/dataRetriever/txpool/memorytests/memory_test.go b/dataRetriever/txpool/memorytests/memory_test.go index a0484f016b8..e4e7f5f68b8 100644 --- a/dataRetriever/txpool/memorytests/memory_test.go +++ b/dataRetriever/txpool/memorytests/memory_test.go @@ -12,6 +12,7 @@ import ( "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-core-go/data/transaction" + "github.com/multiversx/mx-chain-core-go/marshal" "github.com/multiversx/mx-chain-go/dataRetriever" "github.com/multiversx/mx-chain-go/dataRetriever/txpool" "github.com/multiversx/mx-chain-go/storage/storageunit" @@ -35,26 +36,26 @@ func TestShardedTxPool_MemoryFootprint(t *testing.T) { journals = append(journals, runScenario(t, newScenario(200, 1, core.MegabyteSize, "0"), memoryAssertion{200, 200}, memoryAssertion{0, 1})) journals = append(journals, runScenario(t, newScenario(10, 1000, 20480, "0"), memoryAssertion{190, 205}, memoryAssertion{1, 4})) - journals = append(journals, runScenario(t, newScenario(10000, 1, 1024, "0"), memoryAssertion{10, 16}, memoryAssertion{4, 10})) - journals = append(journals, runScenario(t, newScenario(1, 60000, 256, "0"), memoryAssertion{30, 40}, memoryAssertion{10, 16})) - journals = append(journals, runScenario(t, newScenario(10, 10000, 100, "0"), memoryAssertion{36, 52}, memoryAssertion{16, 24})) - journals = append(journals, runScenario(t, newScenario(100000, 1, 1024, "0"), memoryAssertion{120, 138}, memoryAssertion{56, 60})) + journals = append(journals, runScenario(t, newScenario(10000, 1, 1024, "0"), memoryAssertion{10, 16}, memoryAssertion{0, 10})) + journals = append(journals, runScenario(t, newScenario(1, 60000, 256, "0"), memoryAssertion{30, 40}, memoryAssertion{10, 24})) + journals = append(journals, runScenario(t, newScenario(10, 10000, 100, "0"), memoryAssertion{36, 52}, memoryAssertion{16, 36})) + journals = append(journals, runScenario(t, newScenario(100000, 1, 1024, "0"), memoryAssertion{120, 138}, memoryAssertion{32, 60})) // With larger memory footprint - journals = append(journals, runScenario(t, newScenario(100000, 3, 650, "0"), memoryAssertion{290, 335}, memoryAssertion{95, 120})) - journals = append(journals, runScenario(t, newScenario(150000, 2, 650, "0"), memoryAssertion{290, 335}, memoryAssertion{120, 140})) - journals = append(journals, runScenario(t, newScenario(300000, 1, 650, "0"), memoryAssertion{290, 335}, memoryAssertion{170, 190})) - journals = append(journals, runScenario(t, newScenario(30, 10000, 650, "0"), memoryAssertion{290, 335}, memoryAssertion{60, 75})) - journals = append(journals, runScenario(t, newScenario(300, 1000, 650, "0"), memoryAssertion{290, 335}, memoryAssertion{60, 80})) + journals = append(journals, runScenario(t, newScenario(100000, 3, 650, "0"), memoryAssertion{290, 335}, memoryAssertion{80, 148})) + journals = append(journals, runScenario(t, newScenario(150000, 2, 650, "0"), memoryAssertion{290, 335}, memoryAssertion{90, 160})) + journals = append(journals, runScenario(t, newScenario(300000, 1, 650, "0"), memoryAssertion{290, 335}, memoryAssertion{100, 190})) + journals = append(journals, runScenario(t, newScenario(30, 10000, 650, "0"), memoryAssertion{290, 335}, memoryAssertion{60, 132})) + journals = append(journals, runScenario(t, newScenario(300, 1000, 650, "0"), memoryAssertion{290, 335}, memoryAssertion{60, 148})) // Scenarios where destination == me 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() @@ -110,12 +111,9 @@ func newPool() dataRetriever.ShardedDataCacherNotifier { } args := txpool.ArgShardedTxPool{ - Config: config, - TxGasHandler: &txcachemocks.TxGasHandlerMock{ - MinimumGasMove: 50000, - MinimumGasPrice: 200000000000, - GasProcessingDivisor: 100, - }, + Config: config, + TxGasHandler: txcachemocks.NewTxGasHandlerMock(), + Marshalizer: &marshal.GogoProtoMarshalizer{}, NumberOfShards: 2, SelfShardID: 0, } @@ -187,9 +185,10 @@ func createTxWithPayload(senderTag int, nonce int, payloadLength int) *dummyTx { return &dummyTx{ Transaction: transaction.Transaction{ - SndAddr: sender, - Nonce: uint64(nonce), - Data: make([]byte, payloadLength), + SndAddr: sender, + Nonce: uint64(nonce), + Data: make([]byte, payloadLength), + GasLimit: uint64(50000 + 1500*payloadLength), }, hash: hash, } diff --git a/dataRetriever/txpool/mempoolHost.go b/dataRetriever/txpool/mempoolHost.go new file mode 100644 index 00000000000..916dd436cd7 --- /dev/null +++ b/dataRetriever/txpool/mempoolHost.go @@ -0,0 +1,112 @@ +package txpool + +import ( + "bytes" + "math/big" + + "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-core-go/marshal" + "github.com/multiversx/mx-chain-go/dataRetriever" + "github.com/multiversx/mx-chain-go/process" + vmcommon "github.com/multiversx/mx-chain-vm-common-go" + "github.com/multiversx/mx-chain-vm-common-go/parsers" +) + +type argsMempoolHost struct { + txGasHandler txGasHandler + marshalizer marshal.Marshalizer +} + +type mempoolHost struct { + txGasHandler txGasHandler + callArgumentsParser process.CallArgumentsParser + esdtTransferParser vmcommon.ESDTTransferParser +} + +func newMempoolHost(args argsMempoolHost) (*mempoolHost, error) { + if check.IfNil(args.txGasHandler) { + return nil, dataRetriever.ErrNilTxGasHandler + } + if check.IfNil(args.marshalizer) { + return nil, dataRetriever.ErrNilMarshalizer + } + + argsParser := parsers.NewCallArgsParser() + + esdtTransferParser, err := parsers.NewESDTTransferParser(args.marshalizer) + if err != nil { + return nil, err + } + + return &mempoolHost{ + txGasHandler: args.txGasHandler, + callArgumentsParser: argsParser, + esdtTransferParser: esdtTransferParser, + }, nil +} + +// ComputeTxFee computes the fee for a transaction. +func (host *mempoolHost) ComputeTxFee(tx data.TransactionWithFeeHandler) *big.Int { + return host.txGasHandler.ComputeTxFee(tx) +} + +// GetTransferredValue returns the value transferred by a transaction. +func (host *mempoolHost) GetTransferredValue(tx data.TransactionHandler) *big.Int { + value := tx.GetValue() + hasValue := value != nil && value.Sign() != 0 + if hasValue { + // Early exit (optimization): a transaction can either bear a regular value or be a "MultiESDTNFTTransfer". + return value + } + + data := tx.GetData() + hasData := len(data) > 0 + if !hasData { + // Early exit (optimization): no "MultiESDTNFTTransfer" to parse. + return tx.GetValue() + } + + maybeMultiTransfer := bytes.HasPrefix(data, []byte(core.BuiltInFunctionMultiESDTNFTTransfer)) + if !maybeMultiTransfer { + // Early exit (optimization). + return big.NewInt(0) + } + + function, args, err := host.callArgumentsParser.ParseData(string(data)) + if err != nil { + return big.NewInt(0) + } + + if function != core.BuiltInFunctionMultiESDTNFTTransfer { + // Early exit (optimization). + return big.NewInt(0) + } + + esdtTransfers, err := host.esdtTransferParser.ParseESDTTransfers(tx.GetSndAddr(), tx.GetRcvAddr(), function, args) + if err != nil { + return big.NewInt(0) + } + + accumulatedNativeValue := big.NewInt(0) + + for _, transfer := range esdtTransfers.ESDTTransfers { + if transfer.ESDTTokenNonce != 0 { + continue + } + if string(transfer.ESDTTokenName) != vmcommon.EGLDIdentifier { + // We only care about native transfers. + continue + } + + _ = accumulatedNativeValue.Add(accumulatedNativeValue, transfer.ESDTValue) + } + + return accumulatedNativeValue +} + +// IsInterfaceNil returns true if there is no value under the interface +func (host *mempoolHost) IsInterfaceNil() bool { + return host == nil +} diff --git a/dataRetriever/txpool/mempoolHost_test.go b/dataRetriever/txpool/mempoolHost_test.go new file mode 100644 index 00000000000..a013a88fa19 --- /dev/null +++ b/dataRetriever/txpool/mempoolHost_test.go @@ -0,0 +1,182 @@ +package txpool + +import ( + "encoding/hex" + "fmt" + "math/big" + "testing" + + "github.com/multiversx/mx-chain-core-go/core" + "github.com/multiversx/mx-chain-core-go/data/transaction" + "github.com/multiversx/mx-chain-core-go/marshal" + "github.com/multiversx/mx-chain-go/dataRetriever" + "github.com/multiversx/mx-chain-go/testscommon" + "github.com/multiversx/mx-chain-go/testscommon/txcachemocks" + "github.com/stretchr/testify/require" +) + +func TestNewMempoolHost(t *testing.T) { + t.Parallel() + + host, err := newMempoolHost(argsMempoolHost{ + txGasHandler: nil, + marshalizer: &marshal.GogoProtoMarshalizer{}, + }) + require.Nil(t, host) + require.ErrorIs(t, err, dataRetriever.ErrNilTxGasHandler) + + host, err = newMempoolHost(argsMempoolHost{ + txGasHandler: txcachemocks.NewTxGasHandlerMock(), + marshalizer: nil, + }) + require.Nil(t, host) + require.ErrorIs(t, err, dataRetriever.ErrNilMarshalizer) + + host, err = newMempoolHost(argsMempoolHost{ + txGasHandler: txcachemocks.NewTxGasHandlerMock(), + marshalizer: &marshal.GogoProtoMarshalizer{}, + }) + require.NoError(t, err) + require.NotNil(t, host) +} + +func TestMempoolHost_GetTransferredValue(t *testing.T) { + t.Parallel() + + host, err := newMempoolHost(argsMempoolHost{ + txGasHandler: txcachemocks.NewTxGasHandlerMock(), + marshalizer: &marshal.GogoProtoMarshalizer{}, + }) + require.NoError(t, err) + require.NotNil(t, host) + + t.Run("with value", func(t *testing.T) { + value := host.GetTransferredValue(&transaction.Transaction{ + Value: big.NewInt(1000000000000000000), + }) + require.Equal(t, big.NewInt(1000000000000000000), value) + }) + + t.Run("with value and data", func(t *testing.T) { + value := host.GetTransferredValue(&transaction.Transaction{ + Value: big.NewInt(1000000000000000000), + Data: []byte("data"), + }) + require.Equal(t, big.NewInt(1000000000000000000), value) + }) + + t.Run("native transfer within MultiESDTNFTTransfer", func(t *testing.T) { + value := host.GetTransferredValue(&transaction.Transaction{ + SndAddr: testscommon.TestPubKeyAlice, + RcvAddr: testscommon.TestPubKeyAlice, + Data: []byte("MultiESDTNFTTransfer@8049d639e5a6980d1cd2392abcce41029cda74a1563523a202f09641cc2618f8@03@4e46542d313233343536@0a@01@544553542d393837363534@01@01@45474c442d303030303030@@0de0b6b3a7640000"), + }) + require.Equal(t, big.NewInt(1000000000000000000), value) + }) + + t.Run("native transfer within MultiESDTNFTTransfer; transfer & execute", func(t *testing.T) { + value := host.GetTransferredValue(&transaction.Transaction{ + SndAddr: testscommon.TestPubKeyAlice, + RcvAddr: testscommon.TestPubKeyAlice, + Data: []byte("MultiESDTNFTTransfer@00000000000000000500b9353fe8407f87310c87e12fa1ac807f0485da39d152@03@4e46542d313233343536@01@01@4e46542d313233343536@2a@01@45474c442d303030303030@@0de0b6b3a7640000@64756d6d79@07"), + }) + require.Equal(t, big.NewInt(1000000000000000000), value) + }) +} + +func TestBenchmarkMempoolHost_GetTransferredValue(t *testing.T) { + host, err := newMempoolHost(argsMempoolHost{ + txGasHandler: txcachemocks.NewTxGasHandlerMock(), + marshalizer: &marshal.GogoProtoMarshalizer{}, + }) + require.NoError(t, err) + require.NotNil(t, host) + + sw := core.NewStopWatch() + + valueMultiplier := int64(1_000_000_000_000) + + t.Run("numTransactions = 5_000", func(t *testing.T) { + numTransactions := 5_000 + transactions := createMultiESDTNFTTransfersWithNativeTransfer(numTransactions, valueMultiplier) + + sw.Start(t.Name()) + + for i := 0; i < numTransactions; i++ { + tx := transactions[i] + value := host.GetTransferredValue(tx) + require.Equal(t, big.NewInt(int64(i)*valueMultiplier), value) + } + + sw.Stop(t.Name()) + }) + + t.Run("numTransactions = 10_000", func(t *testing.T) { + numTransactions := 10_000 + transactions := createMultiESDTNFTTransfersWithNativeTransfer(numTransactions, valueMultiplier) + + sw.Start(t.Name()) + + for i := 0; i < numTransactions; i++ { + tx := transactions[i] + value := host.GetTransferredValue(tx) + require.Equal(t, big.NewInt(int64(i)*valueMultiplier), value) + } + + sw.Stop(t.Name()) + }) + + t.Run("numTransactions = 20_000", func(t *testing.T) { + numTransactions := 20_000 + transactions := createMultiESDTNFTTransfersWithNativeTransfer(numTransactions, valueMultiplier) + + sw.Start(t.Name()) + + for i := 0; i < numTransactions; i++ { + tx := transactions[i] + value := host.GetTransferredValue(tx) + require.Equal(t, big.NewInt(int64(i)*valueMultiplier), value) + } + + sw.Stop(t.Name()) + }) + + for name, measurement := range sw.GetMeasurementsMap() { + fmt.Printf("%fs (%s)\n", measurement, name) + } + + // (1) + // Vendor ID: GenuineIntel + // Model name: 11th Gen Intel(R) Core(TM) i7-1165G7 @ 2.80GHz + // CPU family: 6 + // Model: 140 + // Thread(s) per core: 2 + // Core(s) per socket: 4 + // + // NOTE: 20% is also due to the require() / assert() calls. + // 0.012993s (TestBenchmarkMempoolHost_GetTransferredValue/numTransactions_=_5_000) + // 0.024580s (TestBenchmarkMempoolHost_GetTransferredValue/numTransactions_=_10_000) + // 0.048808s (TestBenchmarkMempoolHost_GetTransferredValue/numTransactions_=_20_000) +} + +func createMultiESDTNFTTransfersWithNativeTransfer(numTransactions int, valueMultiplier int64) []*transaction.Transaction { + transactions := make([]*transaction.Transaction, 0, numTransactions) + + for i := 0; i < numTransactions; i++ { + nativeValue := big.NewInt(int64(i) * valueMultiplier) + data := fmt.Sprintf( + "MultiESDTNFTTransfer@8049d639e5a6980d1cd2392abcce41029cda74a1563523a202f09641cc2618f8@03@4e46542d313233343536@0a@01@544553542d393837363534@01@01@45474c442d303030303030@@%s", + hex.EncodeToString(nativeValue.Bytes()), + ) + + tx := &transaction.Transaction{ + SndAddr: testscommon.TestPubKeyAlice, + RcvAddr: testscommon.TestPubKeyAlice, + Data: []byte(data), + } + + transactions = append(transactions, tx) + } + + return transactions +} diff --git a/dataRetriever/txpool/shardedTxPool.go b/dataRetriever/txpool/shardedTxPool.go index af09753bd52..0f40817893d 100644 --- a/dataRetriever/txpool/shardedTxPool.go +++ b/dataRetriever/txpool/shardedTxPool.go @@ -27,7 +27,7 @@ type shardedTxPool struct { configPrototypeDestinationMe txcache.ConfigDestinationMe configPrototypeSourceMe txcache.ConfigSourceMe selfShardID uint32 - txGasHandler txcache.TxGasHandler + host txcache.MempoolHost } type txPoolShard struct { @@ -38,24 +38,32 @@ type txPoolShard struct { // NewShardedTxPool creates a new sharded tx pool // Implements "dataRetriever.TxPool" func NewShardedTxPool(args ArgShardedTxPool) (*shardedTxPool, error) { - log.Debug("NewShardedTxPool", "args", args.String()) + log.Debug("NewShardedTxPool", "args.SelfShardID", args.SelfShardID) err := args.verify() if err != nil { return nil, err } + mempoolHost, err := newMempoolHost(argsMempoolHost{ + txGasHandler: args.TxGasHandler, + marshalizer: args.Marshalizer, + }) + if err != nil { + return nil, err + } + halfOfSizeInBytes := args.Config.SizeInBytes / 2 halfOfCapacity := args.Config.Capacity / 2 configPrototypeSourceMe := txcache.ConfigSourceMe{ - NumChunks: args.Config.Shards, - EvictionEnabled: true, - NumBytesThreshold: uint32(halfOfSizeInBytes), - CountThreshold: halfOfCapacity, - NumBytesPerSenderThreshold: args.Config.SizeInBytesPerSender, - CountPerSenderThreshold: args.Config.SizePerSender, - NumSendersToPreemptivelyEvict: dataRetriever.TxPoolNumSendersToPreemptivelyEvict, + NumChunks: args.Config.Shards, + EvictionEnabled: true, + NumBytesThreshold: uint32(halfOfSizeInBytes), + CountThreshold: halfOfCapacity, + NumBytesPerSenderThreshold: args.Config.SizeInBytesPerSender, + CountPerSenderThreshold: args.Config.SizePerSender, + NumItemsToPreemptivelyEvict: storage.TxPoolSourceMeNumItemsToPreemptivelyEvict, } // We do not reserve cross tx cache capacity for [metachain] -> [me] (no transactions), [me] -> me (already reserved above). @@ -66,7 +74,7 @@ func NewShardedTxPool(args ArgShardedTxPool) (*shardedTxPool, error) { NumChunks: args.Config.Shards, MaxNumBytes: uint32(halfOfSizeInBytes) / numCrossTxCaches, MaxNumItems: halfOfCapacity / numCrossTxCaches, - NumItemsToPreemptivelyEvict: storage.TxPoolNumTxsToPreemptivelyEvict, + NumItemsToPreemptivelyEvict: storage.TxPoolDestinationMeNumItemsToPreemptivelyEvict, } shardedTxPoolObject := &shardedTxPool{ @@ -77,7 +85,7 @@ func NewShardedTxPool(args ArgShardedTxPool) (*shardedTxPool, error) { configPrototypeDestinationMe: configPrototypeDestinationMe, configPrototypeSourceMe: configPrototypeSourceMe, selfShardID: args.SelfShardID, - txGasHandler: args.TxGasHandler, + host: mempoolHost, } return shardedTxPoolObject, nil @@ -134,7 +142,7 @@ func (txPool *shardedTxPool) createTxCache(cacheID string) txCache { if isForSenderMe { config := txPool.configPrototypeSourceMe config.Name = cacheID - cache, err := txcache.NewTxCache(config, txPool.txGasHandler) + cache, err := txcache.NewTxCache(config, txPool.host) if err != nil { log.Error("shardedTxPool.createTxCache()", "err", err) return txcache.NewDisabledCache() @@ -188,6 +196,7 @@ func (txPool *shardedTxPool) AddData(key []byte, value interface{}, sizeInBytes func (txPool *shardedTxPool) addTx(tx *txcache.WrappedTransaction, cacheID string) { shard := txPool.getOrCreateShard(cacheID) cache := shard.Cache + _, added := cache.AddTx(tx) if added { txPool.onAdded(tx.TxHash, tx) @@ -228,13 +237,8 @@ func (txPool *shardedTxPool) searchFirstTx(txHash []byte) (tx data.TransactionHa // RemoveData removes the transaction from the pool func (txPool *shardedTxPool) RemoveData(key []byte, cacheID string) { - txPool.removeTx(key, cacheID) -} - -// removeTx removes the transaction from the pool -func (txPool *shardedTxPool) removeTx(txHash []byte, cacheID string) bool { shard := txPool.getOrCreateShard(cacheID) - return shard.Cache.RemoveTxByHash(txHash) + _ = shard.Cache.RemoveTxByHash(key) } // RemoveSetOfDataFromPool removes a bunch of transactions from the pool @@ -244,14 +248,27 @@ func (txPool *shardedTxPool) RemoveSetOfDataFromPool(keys [][]byte, cacheID stri // removeTxBulk removes a bunch of transactions from the pool func (txPool *shardedTxPool) removeTxBulk(txHashes [][]byte, cacheID string) { + shard := txPool.getOrCreateShard(cacheID) + + stopWatch := core.NewStopWatch() + stopWatch.Start("removal") + numRemoved := 0 for _, key := range txHashes { - if txPool.removeTx(key, cacheID) { + if shard.Cache.RemoveTxByHash(key) { numRemoved++ } } - log.Trace("shardedTxPool.removeTxBulk()", "name", cacheID, "numToRemove", len(txHashes), "numRemoved", numRemoved) + stopWatch.Stop("removal") + + // Transactions with lower / equal nonce are also removed, but the counter does not reflect that. + log.Debug("shardedTxPool.removeTxBulk", + "cacheID", cacheID, + "numToRemove", len(txHashes), + "numRemoved", numRemoved, + "duration", stopWatch.GetMeasurement("removal"), + ) } // RemoveDataFromAllShards removes the transaction from the pool (it searches in all shards) diff --git a/dataRetriever/txpool/shardedTxPool_test.go b/dataRetriever/txpool/shardedTxPool_test.go index 6fdaa4676ad..1b3ab585dc3 100644 --- a/dataRetriever/txpool/shardedTxPool_test.go +++ b/dataRetriever/txpool/shardedTxPool_test.go @@ -10,6 +10,7 @@ import ( "github.com/multiversx/mx-chain-core-go/core/check" "github.com/multiversx/mx-chain-core-go/data" "github.com/multiversx/mx-chain-core-go/data/transaction" + "github.com/multiversx/mx-chain-core-go/marshal" "github.com/multiversx/mx-chain-go/dataRetriever" "github.com/multiversx/mx-chain-go/storage/storageunit" "github.com/multiversx/mx-chain-go/testscommon/txcachemocks" @@ -33,11 +34,8 @@ func Test_NewShardedTxPool_WhenBadConfig(t *testing.T) { SizeInBytesPerSender: 40960, Shards: 16, }, - TxGasHandler: &txcachemocks.TxGasHandlerMock{ - MinimumGasMove: 50000, - MinimumGasPrice: 1000000000, - GasProcessingDivisor: 100, - }, + TxGasHandler: txcachemocks.NewTxGasHandlerMock(), + Marshalizer: &marshal.GogoProtoMarshalizer{}, NumberOfShards: 1, } @@ -84,15 +82,11 @@ func Test_NewShardedTxPool_WhenBadConfig(t *testing.T) { require.Errorf(t, err, dataRetriever.ErrNilTxGasHandler.Error()) args = goodArgs - args.TxGasHandler = &txcachemocks.TxGasHandlerMock{ - MinimumGasMove: 50000, - MinimumGasPrice: 0, - GasProcessingDivisor: 1, - } + args.Marshalizer = nil pool, err = NewShardedTxPool(args) require.Nil(t, pool) require.NotNil(t, err) - require.Errorf(t, err, dataRetriever.ErrCacheConfigInvalidEconomics.Error()) + require.Errorf(t, err, dataRetriever.ErrNilMarshalizer.Error()) args = goodArgs args.NumberOfShards = 0 @@ -105,12 +99,9 @@ func Test_NewShardedTxPool_WhenBadConfig(t *testing.T) { func Test_NewShardedTxPool_ComputesCacheConfig(t *testing.T) { config := storageunit.CacheConfig{SizeInBytes: 419430400, SizeInBytesPerSender: 614400, Capacity: 600000, SizePerSender: 1000, Shards: 1} args := ArgShardedTxPool{ - Config: config, - TxGasHandler: &txcachemocks.TxGasHandlerMock{ - MinimumGasMove: 50000, - MinimumGasPrice: 1000000000, - GasProcessingDivisor: 1, - }, + Config: config, + TxGasHandler: txcachemocks.NewTxGasHandlerMock(), + Marshalizer: &marshal.GogoProtoMarshalizer{}, NumberOfShards: 2, } @@ -121,7 +112,6 @@ func Test_NewShardedTxPool_ComputesCacheConfig(t *testing.T) { require.Equal(t, 209715200, int(pool.configPrototypeSourceMe.NumBytesThreshold)) require.Equal(t, 614400, int(pool.configPrototypeSourceMe.NumBytesPerSenderThreshold)) require.Equal(t, 1000, int(pool.configPrototypeSourceMe.CountPerSenderThreshold)) - require.Equal(t, 100, int(pool.configPrototypeSourceMe.NumSendersToPreemptivelyEvict)) require.Equal(t, 300000, int(pool.configPrototypeSourceMe.CountThreshold)) require.Equal(t, 300000, int(pool.configPrototypeDestinationMe.MaxNumItems)) @@ -392,12 +382,9 @@ func Test_routeToCacheUnions(t *testing.T) { Shards: 1, } args := ArgShardedTxPool{ - Config: config, - TxGasHandler: &txcachemocks.TxGasHandlerMock{ - MinimumGasMove: 50000, - MinimumGasPrice: 200000000000, - GasProcessingDivisor: 100, - }, + Config: config, + TxGasHandler: txcachemocks.NewTxGasHandlerMock(), + Marshalizer: &marshal.GogoProtoMarshalizer{}, NumberOfShards: 4, SelfShardID: 42, } @@ -414,8 +401,9 @@ func Test_routeToCacheUnions(t *testing.T) { func createTx(sender string, nonce uint64) data.TransactionHandler { return &transaction.Transaction{ - SndAddr: []byte(sender), - Nonce: nonce, + SndAddr: []byte(sender), + Nonce: nonce, + GasLimit: 50000, } } @@ -435,12 +423,9 @@ func newTxPoolToTest() (dataRetriever.ShardedDataCacherNotifier, error) { Shards: 1, } args := ArgShardedTxPool{ - Config: config, - TxGasHandler: &txcachemocks.TxGasHandlerMock{ - MinimumGasMove: 50000, - MinimumGasPrice: 200000000000, - GasProcessingDivisor: 100, - }, + Config: config, + TxGasHandler: txcachemocks.NewTxGasHandlerMock(), + Marshalizer: &marshal.GogoProtoMarshalizer{}, NumberOfShards: 4, SelfShardID: 0, } diff --git a/epochStart/bootstrap/process_test.go b/epochStart/bootstrap/process_test.go index 2a275c2112b..4f6ef20d19d 100644 --- a/epochStart/bootstrap/process_test.go +++ b/epochStart/bootstrap/process_test.go @@ -931,16 +931,18 @@ func TestEpochStartBootstrap_BootstrapStartInEpochNotEnabled(t *testing.T) { coreComp, cryptoComp := createComponentsForEpochStart() args := createMockEpochStartBootstrapArgs(coreComp, cryptoComp) - err := errors.New("localErr") + localErr := errors.New("localErr") args.LatestStorageDataProvider = &mock.LatestStorageDataProviderStub{ GetCalled: func() (storage.LatestDataFromStorage, error) { - return storage.LatestDataFromStorage{}, err + return storage.LatestDataFromStorage{}, localErr }, } - epochStartProvider, _ := NewEpochStartBootstrap(args) + + epochStartProvider, err := NewEpochStartBootstrap(args) + assert.NoError(t, err) params, err := epochStartProvider.Bootstrap() - assert.Nil(t, err) + assert.NoError(t, err) assert.NotNil(t, params) } @@ -981,7 +983,8 @@ func TestPrepareForEpochZero(t *testing.T) { coreComp, cryptoComp := createComponentsForEpochStart() args := createMockEpochStartBootstrapArgs(coreComp, cryptoComp) - epochStartProvider, _ := NewEpochStartBootstrap(args) + epochStartProvider, err := NewEpochStartBootstrap(args) + assert.Nil(t, err) params, err := epochStartProvider.prepareEpochZero() assert.Nil(t, err) @@ -1017,7 +1020,8 @@ func TestPrepareForEpochZero_NodeInGenesisShouldNotAlterShardID(t *testing.T) { }, } - epochStartProvider, _ := NewEpochStartBootstrap(args) + epochStartProvider, err := NewEpochStartBootstrap(args) + assert.NoError(t, err) params, err := epochStartProvider.prepareEpochZero() assert.NoError(t, err) @@ -1052,7 +1056,8 @@ func TestPrepareForEpochZero_NodeNotInGenesisShouldAlterShardID(t *testing.T) { }, } - epochStartProvider, _ := NewEpochStartBootstrap(args) + epochStartProvider, err := NewEpochStartBootstrap(args) + assert.NoError(t, err) params, err := epochStartProvider.prepareEpochZero() assert.NoError(t, err) diff --git a/factory/disabled/txCoordinator.go b/factory/disabled/txCoordinator.go index a6faee861a2..1bd2e873b9f 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/factory/disabled/txProcessor.go b/factory/disabled/txProcessor.go index d2d04933602..7dd277d65a8 100644 --- a/factory/disabled/txProcessor.go +++ b/factory/disabled/txProcessor.go @@ -20,6 +20,11 @@ func (txProc *TxProcessor) VerifyTransaction(_ *transaction.Transaction) error { return nil } +// VerifyGuardian does nothing as it is disabled +func (txProc *TxProcessor) VerifyGuardian(_ *transaction.Transaction, _ state.UserAccountHandler) error { + return nil +} + // GetSenderAndReceiverAccounts does nothing as it is disabled func (txProc *TxProcessor) GetSenderAndReceiverAccounts(_ *transaction.Transaction) (state.UserAccountHandler, state.UserAccountHandler, error) { return nil, nil, nil diff --git a/factory/processing/blockProcessorCreator_test.go b/factory/processing/blockProcessorCreator_test.go index 35220ed0934..c426bb85f5c 100644 --- a/factory/processing/blockProcessorCreator_test.go +++ b/factory/processing/blockProcessorCreator_test.go @@ -204,7 +204,8 @@ func Test_newBlockProcessorCreatorForMeta(t *testing.T) { componentsMock.SetShardCoordinator(t, args.BootstrapComponents, shardC) - pcf, _ := processComp.NewProcessComponentsFactory(args) + pcf, err := processComp.NewProcessComponentsFactory(args) + require.NoError(t, err) require.NotNil(t, pcf) _, err = pcf.Create() diff --git a/factory/processing/txSimulatorProcessComponents_test.go b/factory/processing/txSimulatorProcessComponents_test.go index 0c919b0ba95..e6fd1f90f6a 100644 --- a/factory/processing/txSimulatorProcessComponents_test.go +++ b/factory/processing/txSimulatorProcessComponents_test.go @@ -25,30 +25,33 @@ func TestManagedProcessComponents_createAPITransactionEvaluator(t *testing.T) { t.Run("invalid VMOutputCacher config should error", func(t *testing.T) { processArgs := components.GetProcessComponentsFactoryArgs(shardCoordinatorForShardID2) processArgs.Config.VMOutputCacher.Type = "invalid" - pcf, _ := processing.NewProcessComponentsFactory(processArgs) + pcf, err := processing.NewProcessComponentsFactory(processArgs) + require.Nil(t, err) apiTransactionEvaluator, vmContainerFactory, err := pcf.CreateAPITransactionEvaluator() - assert.NotNil(t, err) + require.NotNil(t, err) assert.True(t, check.IfNil(apiTransactionEvaluator)) assert.True(t, check.IfNil(vmContainerFactory)) assert.Contains(t, err.Error(), "not supported cache type") }) t.Run("should work for shard", func(t *testing.T) { processArgs := components.GetProcessComponentsFactoryArgs(shardCoordinatorForShardID2) - pcf, _ := processing.NewProcessComponentsFactory(processArgs) + pcf, err := processing.NewProcessComponentsFactory(processArgs) + require.Nil(t, err) apiTransactionEvaluator, vmContainerFactory, err := pcf.CreateAPITransactionEvaluator() - assert.Nil(t, err) + require.Nil(t, err) assert.False(t, check.IfNil(apiTransactionEvaluator)) assert.False(t, check.IfNil(vmContainerFactory)) require.NoError(t, vmContainerFactory.Close()) }) t.Run("should work for metachain", func(t *testing.T) { processArgs := components.GetProcessComponentsFactoryArgs(shardCoordinatorForMetachain) - pcf, _ := processing.NewProcessComponentsFactory(processArgs) + pcf, err := processing.NewProcessComponentsFactory(processArgs) + require.Nil(t, err) apiTransactionEvaluator, vmContainerFactory, err := pcf.CreateAPITransactionEvaluator() - assert.Nil(t, err) + require.Nil(t, err) assert.False(t, check.IfNil(apiTransactionEvaluator)) assert.False(t, check.IfNil(vmContainerFactory)) require.NoError(t, vmContainerFactory.Close()) diff --git a/factory/status/statusComponents.go b/factory/status/statusComponents.go index 985964614d7..9823006adb5 100644 --- a/factory/status/statusComponents.go +++ b/factory/status/statusComponents.go @@ -48,6 +48,7 @@ type StatusComponentsFactoryArgs struct { CryptoComponents factory.CryptoComponentsHolder IsInImportMode bool IsSovereign bool + ESDTPrefix string } type statusComponentsFactory struct { @@ -65,6 +66,7 @@ type statusComponentsFactory struct { cryptoComponents factory.CryptoComponentsHolder isInImportMode bool isSovereign bool + esdtPrefix string } var log = logger.GetOrCreate("factory") @@ -110,6 +112,7 @@ func NewStatusComponentsFactory(args StatusComponentsFactoryArgs) (*statusCompon isInImportMode: args.IsInImportMode, cryptoComponents: args.CryptoComponents, isSovereign: args.IsSovereign, + esdtPrefix: args.ESDTPrefix, }, nil } @@ -242,6 +245,7 @@ func (scf *statusComponentsFactory) makeElasticIndexerArgs() indexerFactory.Args ImportDB: scf.isInImportMode, HeaderMarshaller: scf.coreComponents.InternalMarshalizer(), Sovereign: scf.isSovereign, + ESDTPrefix: scf.esdtPrefix, MainChainElastic: mainChainElastic, } } diff --git a/factory/status/statusComponents_test.go b/factory/status/statusComponents_test.go index cf38580afcf..74b1b48ee4b 100644 --- a/factory/status/statusComponents_test.go +++ b/factory/status/statusComponents_test.go @@ -65,6 +65,7 @@ func createMockStatusComponentsFactoryArgs() statusComp.StatusComponentsFactoryA }, IsInImportMode: false, IsSovereign: false, + ESDTPrefix: "", } } diff --git a/genesis/process/disabled/feeHandler.go b/genesis/process/disabled/feeHandler.go index 1fc34bbc2b5..0b3051cf845 100644 --- a/genesis/process/disabled/feeHandler.go +++ b/genesis/process/disabled/feeHandler.go @@ -12,6 +12,11 @@ import ( type FeeHandler struct { } +// ComputeGasUnitsFromRefundValue return 0 +func (fh *FeeHandler) ComputeGasUnitsFromRefundValue(_ data.TransactionWithFeeHandler, _ *big.Int, _ uint32) uint64 { + return 0 +} + // GasPriceModifier returns 1.0 func (fh *FeeHandler) GasPriceModifier() float64 { return 1.0 diff --git a/go.mod b/go.mod index d156d15e9fb..f28fa96002a 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.20241209142414-762196f8dd06 + github.com/multiversx/mx-chain-core-go v1.2.25-0.20250131122047-725dd68e256f github.com/multiversx/mx-chain-crypto-go v1.2.12 - github.com/multiversx/mx-chain-es-indexer-go v1.7.11-0.20250128112625-86bc0754b1e9 + github.com/multiversx/mx-chain-es-indexer-go v1.7.15-0.20250131125841-73868f5ca49c 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 + github.com/multiversx/mx-chain-storage-go v1.0.19 github.com/multiversx/mx-chain-vm-common-go v1.5.17-0.20241119132002-2fa80c5ec516 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 9d0187353b0..89b8d548ae2 100644 --- a/go.sum +++ b/go.sum @@ -387,18 +387,18 @@ 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.20241209142414-762196f8dd06 h1:3Sog/pzWhtQ7UKHTaJhK50EjSqlkxTOD4JKWmJZzr50= -github.com/multiversx/mx-chain-core-go v1.2.24-0.20241209142414-762196f8dd06/go.mod h1:P/YBoFnt25XUaCQ7Q/SD15vhnc9yV5JDhHxyFO9P8Z0= +github.com/multiversx/mx-chain-core-go v1.2.25-0.20250131122047-725dd68e256f h1:iyDk0Zk0n4pHZnrdX30SxevTy+xg9GcEyAmBss45Nfs= +github.com/multiversx/mx-chain-core-go v1.2.25-0.20250131122047-725dd68e256f/go.mod h1:P/YBoFnt25XUaCQ7Q/SD15vhnc9yV5JDhHxyFO9P8Z0= 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.20250128112625-86bc0754b1e9 h1:rQL4GS2fLvSHzPqbZ08emtfh+IzMkFJOlwtNenyHtAI= -github.com/multiversx/mx-chain-es-indexer-go v1.7.11-0.20250128112625-86bc0754b1e9/go.mod h1:i5xoCUsrHteffxx3yfR8bj9tmpP/DtastZGOc/KRAoY= +github.com/multiversx/mx-chain-es-indexer-go v1.7.15-0.20250131125841-73868f5ca49c h1:Qm84hhybDLTeS+BLebFYgmGJpkX+AUnzZsUC+u9KzBU= +github.com/multiversx/mx-chain-es-indexer-go v1.7.15-0.20250131125841-73868f5ca49c/go.mod h1:u3P9JM8JWPRcYW3X0Lsw06Nf6a6M7cS42XwT4Au/Ar8= 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= 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.16 h1:l2lJq+EAN3YwLbjJrnoKfFd1/1Xmo9DcAUECND2obLs= -github.com/multiversx/mx-chain-storage-go v1.0.16/go.mod h1:uM/z7YyqTOD3wgyH8TfapyEl5sb+7x/Jaxne4cfG4HI= +github.com/multiversx/mx-chain-storage-go v1.0.19 h1:2R35MoSXcuNJOFmV5xEhcXqiEGZw6AYGy9R8J9KH66Q= +github.com/multiversx/mx-chain-storage-go v1.0.19/go.mod h1:Pb/BuVmiFqO66DSZO16KFkSUeom94x3e3Q9IloBvkYI= github.com/multiversx/mx-chain-vm-common-go v1.5.17-0.20241119132002-2fa80c5ec516 h1:PToP1B7aFkXjQBOF5MDCCvsc+ffWVqqBHtmemfCwVA8= github.com/multiversx/mx-chain-vm-common-go v1.5.17-0.20241119132002-2fa80c5ec516/go.mod h1:C7KVj6/+TAhxDjgY7oAMO5wSj7WbBYIJ5TCMzmxk2w0= github.com/multiversx/mx-chain-vm-go v1.5.37 h1:Iy3KCvM+DOq1f9UPA7uYK/rI3ZbBOXc2CVNO2/vm5zw= diff --git a/integrationTests/chainSimulator/mempool/mempool_test.go b/integrationTests/chainSimulator/mempool/mempool_test.go new file mode 100644 index 00000000000..704be8e40fb --- /dev/null +++ b/integrationTests/chainSimulator/mempool/mempool_test.go @@ -0,0 +1,331 @@ +package mempool + +import ( + "math/big" + "testing" + "time" + + "github.com/multiversx/mx-chain-core-go/data/transaction" + "github.com/multiversx/mx-chain-go/config" + "github.com/multiversx/mx-chain-go/node/chainSimulator/configs" + "github.com/multiversx/mx-chain-go/storage" + "github.com/stretchr/testify/require" +) + +func TestMempoolWithChainSimulator_Selection(t *testing.T) { + if testing.Short() { + t.Skip("this is not a short test") + } + + numSenders := 10000 + numTransactionsPerSender := 3 + shard := 0 + + simulator := startChainSimulator(t, func(cfg *config.Configs) {}) + defer simulator.Close() + + participants := createParticipants(t, simulator, numSenders) + noncesTracker := newNoncesTracker() + + transactions := make([]*transaction.Transaction, 0, numSenders*numTransactionsPerSender) + + for i := 0; i < numSenders; i++ { + sender := participants.sendersByShard[shard][i] + receiver := participants.receiverByShard[shard] + + for j := 0; j < numTransactionsPerSender; j++ { + tx := &transaction.Transaction{ + Nonce: noncesTracker.getThenIncrementNonce(sender), + Value: oneQuarterOfEGLD, + SndAddr: sender.Bytes, + RcvAddr: receiver.Bytes, + Data: []byte{}, + GasLimit: 50_000, + GasPrice: 1_000_000_000, + ChainID: []byte(configs.ChainID), + Version: 2, + Signature: []byte("signature"), + } + + transactions = append(transactions, tx) + } + } + + sendTransactions(t, simulator, transactions) + time.Sleep(durationWaitAfterSendMany) + require.Equal(t, 30_000, getNumTransactionsInPool(simulator, shard)) + + selectedTransactions, gas := selectTransactions(t, simulator, shard) + require.Equal(t, 30_000, len(selectedTransactions)) + require.Equal(t, 50_000*30_000, int(gas)) + + err := simulator.GenerateBlocks(1) + require.Nil(t, err) + require.Equal(t, 27_756, getNumTransactionsInCurrentBlock(simulator, shard)) + + selectedTransactions, gas = selectTransactions(t, simulator, shard) + require.Equal(t, 30_000-27_756, len(selectedTransactions)) + require.Equal(t, 50_000*(30_000-27_756), int(gas)) +} + +func TestMempoolWithChainSimulator_Selection_WhenUsersHaveZeroBalance_WithRelayedV3(t *testing.T) { + if testing.Short() { + t.Skip("this is not a short test") + } + + shard := 0 + + simulator := startChainSimulator(t, func(cfg *config.Configs) {}) + defer simulator.Close() + + err := simulator.GenerateBlocksUntilEpochIsReached(2) + require.NoError(t, err) + + relayer, err := simulator.GenerateAndMintWalletAddress(uint32(shard), oneEGLD) + require.NoError(t, err) + + receiver, err := simulator.GenerateAndMintWalletAddress(uint32(shard), big.NewInt(0)) + require.NoError(t, err) + + alice, err := simulator.GenerateAndMintWalletAddress(uint32(shard), big.NewInt(0)) + require.NoError(t, err) + + bob, err := simulator.GenerateAndMintWalletAddress(uint32(shard), big.NewInt(0)) + require.NoError(t, err) + + err = simulator.GenerateBlocks(1) + require.Nil(t, err) + + noncesTracker := newNoncesTracker() + transactions := make([]*transaction.Transaction, 0) + + // Transfer (executable, invalid) from Alice (relayed) + transactions = append(transactions, &transaction.Transaction{ + Nonce: noncesTracker.getThenIncrementNonce(alice), + Value: oneQuarterOfEGLD, + SndAddr: alice.Bytes, + RcvAddr: receiver.Bytes, + RelayerAddr: relayer.Bytes, + Data: []byte{}, + GasLimit: 100_000, + GasPrice: 1_000_000_002, + ChainID: []byte(configs.ChainID), + Version: 2, + Signature: []byte("signature"), + RelayerSignature: []byte("signature"), + }) + + // Contract call from Bob (relayed) + transactions = append(transactions, &transaction.Transaction{ + Nonce: noncesTracker.getThenIncrementNonce(bob), + Value: big.NewInt(0), + SndAddr: bob.Bytes, + RcvAddr: receiver.Bytes, + RelayerAddr: relayer.Bytes, + Data: []byte("hello"), + GasLimit: 100_000 + 5*1500, + GasPrice: 1_000_000_001, + ChainID: []byte(configs.ChainID), + Version: 2, + Signature: []byte("signature"), + RelayerSignature: []byte("signature"), + }) + + sendTransactions(t, simulator, transactions) + time.Sleep(durationWaitAfterSendSome) + require.Equal(t, 2, getNumTransactionsInPool(simulator, shard)) + + selectedTransactions, _ := selectTransactions(t, simulator, shard) + require.Equal(t, 2, len(selectedTransactions)) + require.Equal(t, alice.Bytes, selectedTransactions[0].Tx.GetSndAddr()) + require.Equal(t, bob.Bytes, selectedTransactions[1].Tx.GetSndAddr()) + + err = simulator.GenerateBlocks(1) + require.Nil(t, err) + require.Equal(t, 2, getNumTransactionsInCurrentBlock(simulator, shard)) + + require.Equal(t, "invalid", getTransaction(t, simulator, shard, selectedTransactions[0].TxHash).Status.String()) + require.Equal(t, "success", getTransaction(t, simulator, shard, selectedTransactions[1].TxHash).Status.String()) +} + +func TestMempoolWithChainSimulator_Selection_WhenInsufficientBalanceForFee_WithRelayedV3(t *testing.T) { + if testing.Short() { + t.Skip("this is not a short test") + } + + numSenders := 3 + shard := 0 + + simulator := startChainSimulator(t, func(cfg *config.Configs) {}) + defer simulator.Close() + + err := simulator.GenerateBlocksUntilEpochIsReached(2) + require.NoError(t, err) + + participants := createParticipants(t, simulator, numSenders) + noncesTracker := newNoncesTracker() + + alice := participants.sendersByShard[shard][0] + bob := participants.sendersByShard[shard][1] + carol := participants.sendersByShard[shard][2] + relayer := participants.relayerByShard[shard] + receiver := participants.receiverByShard[shard] + + transactions := make([]*transaction.Transaction, 0) + + // Consume most of relayer's balance. Keep an amount that is enough for the fee of two simple transfer transactions. + currentRelayerBalance := int64(1000000000000000000) + feeForTransfer := int64(50_000 * 1_000_000_004) + feeForRelayingTransactionsOfAliceAndBob := int64(100_000*1_000_000_003 + 100_000*1_000_000_002) + + transactions = append(transactions, &transaction.Transaction{ + Nonce: noncesTracker.getThenIncrementNonce(relayer), + Value: big.NewInt(currentRelayerBalance - feeForTransfer - feeForRelayingTransactionsOfAliceAndBob), + SndAddr: relayer.Bytes, + RcvAddr: receiver.Bytes, + Data: []byte{}, + GasLimit: 50_000, + GasPrice: 1_000_000_004, + ChainID: []byte(configs.ChainID), + Version: 2, + Signature: []byte("signature"), + }) + + // Transfer from Alice (relayed) + transactions = append(transactions, &transaction.Transaction{ + Nonce: noncesTracker.getThenIncrementNonce(alice), + Value: oneQuarterOfEGLD, + SndAddr: alice.Bytes, + RcvAddr: receiver.Bytes, + RelayerAddr: relayer.Bytes, + Data: []byte{}, + GasLimit: 100_000, + GasPrice: 1_000_000_003, + ChainID: []byte(configs.ChainID), + Version: 2, + Signature: []byte("signature"), + RelayerSignature: []byte("signature"), + }) + + // Transfer from Bob (relayed) + transactions = append(transactions, &transaction.Transaction{ + Nonce: noncesTracker.getThenIncrementNonce(bob), + Value: oneQuarterOfEGLD, + SndAddr: bob.Bytes, + RcvAddr: receiver.Bytes, + RelayerAddr: relayer.Bytes, + Data: []byte{}, + GasLimit: 100_000, + GasPrice: 1_000_000_002, + ChainID: []byte(configs.ChainID), + Version: 2, + Signature: []byte("signature"), + RelayerSignature: []byte("signature"), + }) + + // Transfer from Carol (relayed) - this one should not be selected due to insufficient balance (of the relayer) + transactions = append(transactions, &transaction.Transaction{ + Nonce: noncesTracker.getThenIncrementNonce(carol), + Value: oneQuarterOfEGLD, + SndAddr: carol.Bytes, + RcvAddr: receiver.Bytes, + RelayerAddr: relayer.Bytes, + Data: []byte{}, + GasLimit: 100_000, + GasPrice: 1_000_000_001, + ChainID: []byte(configs.ChainID), + Version: 2, + Signature: []byte("signature"), + RelayerSignature: []byte("signature"), + }) + + sendTransactions(t, simulator, transactions) + time.Sleep(durationWaitAfterSendSome) + require.Equal(t, 4, getNumTransactionsInPool(simulator, shard)) + + selectedTransactions, _ := selectTransactions(t, simulator, shard) + require.Equal(t, 3, len(selectedTransactions)) + require.Equal(t, relayer.Bytes, selectedTransactions[0].Tx.GetSndAddr()) + require.Equal(t, alice.Bytes, selectedTransactions[1].Tx.GetSndAddr()) + require.Equal(t, bob.Bytes, selectedTransactions[2].Tx.GetSndAddr()) +} + +func TestMempoolWithChainSimulator_Eviction(t *testing.T) { + if testing.Short() { + t.Skip("this is not a short test") + } + + numSenders := 10000 + numTransactionsPerSender := 30 + shard := 0 + + simulator := startChainSimulator(t, func(cfg *config.Configs) {}) + defer simulator.Close() + + participants := createParticipants(t, simulator, numSenders) + noncesTracker := newNoncesTracker() + + transactions := make([]*transaction.Transaction, 0, numSenders*numTransactionsPerSender) + + for i := 0; i < numSenders; i++ { + sender := participants.sendersByShard[shard][i] + receiver := participants.receiverByShard[shard] + + for j := 0; j < numTransactionsPerSender; j++ { + tx := &transaction.Transaction{ + Nonce: noncesTracker.getThenIncrementNonce(sender), + Value: oneQuarterOfEGLD, + SndAddr: sender.Bytes, + RcvAddr: receiver.Bytes, + Data: []byte{}, + GasLimit: 50_000, + GasPrice: 1_000_000_000, + ChainID: []byte(configs.ChainID), + Version: 2, + Signature: []byte("signature"), + } + + transactions = append(transactions, tx) + } + } + + sendTransactions(t, simulator, transactions) + time.Sleep(durationWaitAfterSendMany) + require.Equal(t, 300_000, getNumTransactionsInPool(simulator, shard)) + + // Send one more transaction (fill up the mempool) + sendTransaction(t, simulator, &transaction.Transaction{ + Nonce: 42, + Value: oneEGLD, + SndAddr: participants.sendersByShard[shard][7].Bytes, + RcvAddr: participants.receiverByShard[shard].Bytes, + Data: []byte{}, + GasLimit: 50000, + GasPrice: 1_000_000_000, + ChainID: []byte(configs.ChainID), + Version: 2, + Signature: []byte("signature"), + }) + + time.Sleep(durationWaitAfterSendSome) + require.Equal(t, 300_001, getNumTransactionsInPool(simulator, shard)) + + // Send one more transaction to trigger eviction + sendTransaction(t, simulator, &transaction.Transaction{ + Nonce: 42, + Value: oneEGLD, + SndAddr: participants.sendersByShard[shard][7].Bytes, + RcvAddr: participants.receiverByShard[shard].Bytes, + Data: []byte{}, + GasLimit: 50000, + GasPrice: 1_000_000_000, + ChainID: []byte(configs.ChainID), + Version: 2, + Signature: []byte("signature"), + }) + + time.Sleep(2 * time.Second) + + expectedNumTransactionsInPool := 300_000 + 1 + 1 - int(storage.TxPoolSourceMeNumItemsToPreemptivelyEvict) + require.Equal(t, expectedNumTransactionsInPool, getNumTransactionsInPool(simulator, shard)) +} diff --git a/integrationTests/chainSimulator/mempool/testutils_test.go b/integrationTests/chainSimulator/mempool/testutils_test.go new file mode 100644 index 00000000000..3d4a0afd5f7 --- /dev/null +++ b/integrationTests/chainSimulator/mempool/testutils_test.go @@ -0,0 +1,193 @@ +package mempool + +import ( + "encoding/hex" + "math/big" + "strconv" + "testing" + "time" + + "github.com/multiversx/mx-chain-core-go/core" + "github.com/multiversx/mx-chain-core-go/data/transaction" + "github.com/multiversx/mx-chain-go/config" + testsChainSimulator "github.com/multiversx/mx-chain-go/integrationTests/chainSimulator" + "github.com/multiversx/mx-chain-go/node/chainSimulator" + "github.com/multiversx/mx-chain-go/node/chainSimulator/components/api" + "github.com/multiversx/mx-chain-go/node/chainSimulator/dtos" + "github.com/multiversx/mx-chain-go/process" + "github.com/multiversx/mx-chain-go/process/block/preprocess" + "github.com/multiversx/mx-chain-go/storage/txcache" + "github.com/multiversx/mx-chain-go/testscommon" + "github.com/stretchr/testify/require" +) + +var ( + oneEGLD = big.NewInt(1000000000000000000) + oneQuarterOfEGLD = big.NewInt(250000000000000000) + durationWaitAfterSendMany = 1500 * time.Millisecond + durationWaitAfterSendSome = 50 * time.Millisecond +) + +func startChainSimulator(t *testing.T, alterConfigsFunction func(cfg *config.Configs)) testsChainSimulator.ChainSimulator { + simulator, err := chainSimulator.NewChainSimulator(chainSimulator.ArgsChainSimulator{ + BypassTxSignatureCheck: true, + TempDir: t.TempDir(), + PathToInitialConfig: "../../../cmd/node/config/", + NumOfShards: 1, + GenesisTimestamp: time.Now().Unix(), + RoundDurationInMillis: uint64(4000), + RoundsPerEpoch: core.OptionalUint64{ + HasValue: true, + Value: 10, + }, + ApiInterface: api.NewNoApiInterface(), + MinNodesPerShard: 1, + MetaChainMinNodes: 1, + NumNodesWaitingListMeta: 0, + NumNodesWaitingListShard: 0, + AlterConfigsFunction: alterConfigsFunction, + }) + require.NoError(t, err) + require.NotNil(t, simulator) + + err = simulator.GenerateBlocksUntilEpochIsReached(1) + require.NoError(t, err) + + return simulator +} + +type participantsHolder struct { + sendersByShard map[int][]dtos.WalletAddress + relayerByShard map[int]dtos.WalletAddress + receiverByShard map[int]dtos.WalletAddress +} + +func newParticipantsHolder() *participantsHolder { + return &participantsHolder{ + sendersByShard: make(map[int][]dtos.WalletAddress), + relayerByShard: make(map[int]dtos.WalletAddress), + receiverByShard: make(map[int]dtos.WalletAddress), + } +} + +func createParticipants(t *testing.T, simulator testsChainSimulator.ChainSimulator, numSendersPerShard int) *participantsHolder { + numShards := int(simulator.GetNodeHandler(0).GetShardCoordinator().NumberOfShards()) + participants := newParticipantsHolder() + + for shard := 0; shard < numShards; shard++ { + senders := make([]dtos.WalletAddress, 0, numSendersPerShard) + + for i := 0; i < numSendersPerShard; i++ { + sender, err := simulator.GenerateAndMintWalletAddress(uint32(shard), oneEGLD) + require.NoError(t, err) + + senders = append(senders, sender) + } + + relayer, err := simulator.GenerateAndMintWalletAddress(uint32(shard), oneEGLD) + require.NoError(t, err) + + receiver, err := simulator.GenerateAndMintWalletAddress(uint32(shard), big.NewInt(0)) + require.NoError(t, err) + + participants.sendersByShard[shard] = senders + participants.relayerByShard[shard] = relayer + participants.receiverByShard[shard] = receiver + } + + err := simulator.GenerateBlocks(1) + require.Nil(t, err) + + return participants +} + +type noncesTracker struct { + nonceByAddress map[string]uint64 +} + +func newNoncesTracker() *noncesTracker { + return &noncesTracker{ + nonceByAddress: make(map[string]uint64), + } +} + +func (tracker *noncesTracker) getThenIncrementNonce(address dtos.WalletAddress) uint64 { + nonce, ok := tracker.nonceByAddress[address.Bech32] + if !ok { + tracker.nonceByAddress[address.Bech32] = 0 + } + + tracker.nonceByAddress[address.Bech32]++ + return nonce +} + +func sendTransactions(t *testing.T, simulator testsChainSimulator.ChainSimulator, transactions []*transaction.Transaction) { + transactionsBySenderShard := make(map[int][]*transaction.Transaction) + shardCoordinator := simulator.GetNodeHandler(0).GetShardCoordinator() + + for _, tx := range transactions { + shard := int(shardCoordinator.ComputeId(tx.SndAddr)) + transactionsBySenderShard[shard] = append(transactionsBySenderShard[shard], tx) + } + + for shard, transactionsFromShard := range transactionsBySenderShard { + node := simulator.GetNodeHandler(uint32(shard)) + + for _, tx := range transactionsFromShard { + err := node.GetFacadeHandler().ValidateTransaction(tx) + require.NoError(t, err) + } + + numSent, err := node.GetFacadeHandler().SendBulkTransactions(transactionsFromShard) + + require.NoError(t, err) + require.Equal(t, len(transactionsFromShard), int(numSent)) + } +} + +func sendTransaction(t *testing.T, simulator testsChainSimulator.ChainSimulator, tx *transaction.Transaction) { + sendTransactions(t, simulator, []*transaction.Transaction{tx}) +} + +func selectTransactions(t *testing.T, simulator testsChainSimulator.ChainSimulator, shard int) ([]*txcache.WrappedTransaction, uint64) { + shardAsString := strconv.Itoa(shard) + node := simulator.GetNodeHandler(uint32(shard)) + accountsAdapter := node.GetStateComponents().AccountsAdapter() + poolsHolder := node.GetDataComponents().Datapool().Transactions() + + selectionSession, err := preprocess.NewSelectionSession(preprocess.ArgsSelectionSession{ + AccountsAdapter: accountsAdapter, + TransactionsProcessor: &testscommon.TxProcessorStub{}, + }) + require.NoError(t, err) + + mempool := poolsHolder.ShardDataStore(shardAsString).(*txcache.TxCache) + + selectedTransactions, gas := mempool.SelectTransactions( + selectionSession, + process.TxCacheSelectionGasRequested, + process.TxCacheSelectionMaxNumTxs, + process.TxCacheSelectionLoopMaximumDuration, + ) + + return selectedTransactions, gas +} + +func getNumTransactionsInPool(simulator testsChainSimulator.ChainSimulator, shard int) int { + node := simulator.GetNodeHandler(uint32(shard)) + poolsHolder := node.GetDataComponents().Datapool().Transactions() + return int(poolsHolder.GetCounts().GetTotal()) +} + +func getNumTransactionsInCurrentBlock(simulator testsChainSimulator.ChainSimulator, shard int) int { + node := simulator.GetNodeHandler(uint32(shard)) + currentBlock := node.GetDataComponents().Blockchain().GetCurrentBlockHeader() + return int(currentBlock.GetTxCount()) +} + +func getTransaction(t *testing.T, simulator testsChainSimulator.ChainSimulator, shard int, hash []byte) *transaction.ApiTransactionResult { + hashAsHex := hex.EncodeToString(hash) + transaction, err := simulator.GetNodeHandler(uint32(shard)).GetFacadeHandler().GetTransaction(hashAsHex, true) + require.NoError(t, err) + return transaction +} diff --git a/integrationTests/chainSimulator/relayedTx/relayedTx_test.go b/integrationTests/chainSimulator/relayedTx/relayedTx_test.go index f7c0f74649b..b7426c63f5f 100644 --- a/integrationTests/chainSimulator/relayedTx/relayedTx_test.go +++ b/integrationTests/chainSimulator/relayedTx/relayedTx_test.go @@ -1,15 +1,18 @@ package relayedTx import ( + "crypto/rand" "encoding/hex" "encoding/json" "math/big" + "strconv" "strings" "testing" "time" "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-core-go/data/transaction" + "github.com/multiversx/mx-chain-go/common" "github.com/multiversx/mx-chain-go/config" testsChainSimulator "github.com/multiversx/mx-chain-go/integrationTests/chainSimulator" "github.com/multiversx/mx-chain-go/integrationTests/vm/wasm" @@ -17,6 +20,11 @@ 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/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" ) @@ -25,27 +33,717 @@ 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 + guardAccountCost = 250_000 + extraGasLimitForGuarded = minGasLimit ) var ( oneEGLD = big.NewInt(1000000000000000000) ) +func TestRelayedV3WithChainSimulator(t *testing.T) { + t.Run("sender == relayer move balance should consume fee", testRelayedV3RelayedBySenderMoveBalance()) + t.Run("receiver == relayer move balance should consume fee", testRelayedV3RelayedByReceiverMoveBalance()) + 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)) + t.Run("cross shard move balance, invalid gas", testRelayedV3MoveInvalidGasLimit(0, 1)) + + t.Run("successful intra shard sc call with refunds, existing sender", testRelayedV3ScCall(0, 0, true, false)) + t.Run("successful intra shard sc call with refunds, existing sender, relayed by sender", testRelayedV3ScCall(0, 0, true, true)) + t.Run("successful intra shard sc call with refunds, new sender", testRelayedV3ScCall(0, 0, false, false)) + t.Run("successful cross shard sc call with refunds, existing sender", testRelayedV3ScCall(0, 1, true, false)) + t.Run("successful cross shard sc call with refunds, existing sender, relayed by sender", testRelayedV3ScCall(0, 1, true, true)) + t.Run("successful cross shard sc call with refunds, new sender", testRelayedV3ScCall(0, 1, false, 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)) + t.Run("cross shard sc call, invalid method", testRelayedV3ScCallInvalidMethod(0, 1)) + + t.Run("create new delegation contract", testRelayedV3MetaInteraction()) +} + +func testRelayedV3MoveBalance( + relayerShard uint32, + destinationShard uint32, + extraGas bool, + guardedTx 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) + + 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 + } + 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) + + isIntraShard := relayerShard == destinationShard + if isIntraShard { + 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) + 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(senderBalanceBefore, senderBalanceAfter) + require.Equal(t, oneEGLD.String(), senderBalanceDiff.String()) + + // check receiver balance + receiverBalanceAfter := getBalance(t, cs, receiver) + require.Equal(t, oneEGLD.String(), receiverBalanceAfter.String()) + + // check scrs, should be none + require.Zero(t, len(result.SmartContractResults)) + + // check intra shard logs, should be none + require.Nil(t, result.Logs) + + if extraGas && isIntraShard { + require.NotNil(t, result.Receipt) + } + } +} + +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, + existingSenderWithBalance bool, + relayedBySender 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) + relayerInitialBalance := initialBalance + + sender, senderInitialBalance := prepareSender(t, cs, existingSenderWithBalance, relayerShard, initialBalance) + if relayedBySender { + relayer = sender + relayerInitialBalance = senderInitialBalance + } + + 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, 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(relayerInitialBalance, relayerBalanceAfter) + require.Equal(t, fee.String(), relayerFee.String()) + + // check sender balance, only if the tx was not relayed by sender + if !relayedBySender { + senderBalanceAfter := getBalance(t, cs, sender) + require.Equal(t, senderInitialBalance.String(), senderBalanceAfter.String()) + } + + // check owner balance + _, 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()) + + // check scrs + require.Equal(t, 1, len(result.SmartContractResults)) + for _, scr := range result.SmartContractResults { + checkSCRSucceeded(t, cs, scr) + } + } +} + +func testRelayedV3RelayedBySenderMoveBalance() 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)) + + sender, 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) + senderBalanceBefore := getBalance(t, cs, sender) + + gasLimit := minGasLimit * 2 + relayedTx := generateRelayedV3Transaction(sender.Bytes, senderNonce, sender.Bytes, sender.Bytes, big.NewInt(0), "", uint64(gasLimit)) + + result, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(relayedTx, maxNumOfBlocksToGenerateWhenExecutingTx) + require.NoError(t, err) + + // check fee fields + initiallyPaidFee, fee, gasUsed := computeTxGasAndFeeBasedOnRefund(result, big.NewInt(0), true, false) + require.Equal(t, initiallyPaidFee.String(), result.InitiallyPaidFee) + require.Equal(t, fee.String(), result.Fee) + require.Equal(t, gasUsed, result.GasUsed) + + // check sender balance + expectedFee := core.SafeMul(uint64(gasLimit), uint64(minGasPrice)) + senderBalanceAfter := getBalance(t, cs, sender) + senderBalanceDiff := big.NewInt(0).Sub(senderBalanceBefore, senderBalanceAfter) + require.Equal(t, expectedFee.String(), senderBalanceDiff.String()) + + // check scrs, should be none + require.Zero(t, len(result.SmartContractResults)) + + // check intra shard logs, should be none + require.Nil(t, result.Logs) + } +} + +func testRelayedV3RelayedByReceiverMoveBalance() 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)) + + sender, err := cs.GenerateAndMintWalletAddress(0, initialBalance) + require.NoError(t, err) + + receiver, 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) + receiverBalanceBefore := getBalance(t, cs, receiver) + + gasLimit := minGasLimit * 2 + relayedTx := generateRelayedV3Transaction(sender.Bytes, senderNonce, receiver.Bytes, receiver.Bytes, big.NewInt(0), "", uint64(gasLimit)) + + result, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(relayedTx, maxNumOfBlocksToGenerateWhenExecutingTx) + require.NoError(t, err) + + // check fee fields + initiallyPaidFee, fee, gasUsed := computeTxGasAndFeeBasedOnRefund(result, big.NewInt(0), true, false) + require.Equal(t, initiallyPaidFee.String(), result.InitiallyPaidFee) + require.Equal(t, fee.String(), result.Fee) + require.Equal(t, gasUsed, result.GasUsed) + + // check sender balance + senderBalanceAfter := getBalance(t, cs, sender) + require.Equal(t, senderBalanceAfter.String(), initialBalance.String()) + + // check receiver balance + expectedFee := core.SafeMul(uint64(gasLimit), uint64(minGasPrice)) + receiverBalanceAfter := getBalance(t, cs, receiver) + receiverBalanceDiff := big.NewInt(0).Sub(receiverBalanceBefore, receiverBalanceAfter) + require.Equal(t, receiverBalanceDiff.String(), expectedFee.String()) + + // check scrs, should be none + require.Zero(t, len(result.SmartContractResults)) + + // check intra shard logs, should be none + require.Nil(t, result.Logs) + } +} + +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, +) 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) + + 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 + } + + 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, 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) + + 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 + } + + 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, 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 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 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, txData, 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(txData)*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") } - 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 +774,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)) @@ -257,6 +955,37 @@ func TestRelayedTransactionFeeField(t *testing.T) { }) } +func TestRegularMoveBalanceWithRefundReceipt(t *testing.T) { + if testing.Short() { + t.Skip("this is not a short test") + } + + cs := startChainSimulator(t, func(cfg *config.Configs) {}) + defer cs.Close() + + initialBalance := big.NewInt(0).Mul(oneEGLD, big.NewInt(10)) + + sender, 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) + + extraGas := uint64(minGasLimit) + gasLimit := minGasLimit + extraGas + tx := generateTransaction(sender.Bytes, senderNonce, sender.Bytes, oneEGLD, "", gasLimit) + + result, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(tx, maxNumOfBlocksToGenerateWhenExecutingTx) + require.NoError(t, err) + + require.NotNil(t, result.Receipt) + expectedGasRefunded := core.SafeMul(extraGas, minGasPrice/deductionFactor) + require.Equal(t, expectedGasRefunded.String(), result.Receipt.Value.String()) +} + func startChainSimulator( t *testing.T, alterConfigsFunction func(cfg *config.Configs), @@ -291,6 +1020,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 +1061,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 +1078,99 @@ 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, + 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 common.IsRelayedTxV3(initialTx) { + 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/factory/bootstrapComponents/bootstrapComponents_test.go b/integrationTests/factory/bootstrapComponents/bootstrapComponents_test.go index 1df03c549fc..79caa8949ae 100644 --- a/integrationTests/factory/bootstrapComponents/bootstrapComponents_test.go +++ b/integrationTests/factory/bootstrapComponents/bootstrapComponents_test.go @@ -42,7 +42,13 @@ func TestBootstrapComponents_Create_Close_ShouldWork(t *testing.T) { require.Nil(t, err) managedNetworkComponents, err := nr.CreateManagedNetworkComponents(managedCoreComponents, managedStatusCoreComponents, managedCryptoComponents) require.Nil(t, err) - managedBootstrapComponents, err := nr.CreateManagedBootstrapComponents(managedStatusCoreComponents, managedCoreComponents, managedCryptoComponents, managedNetworkComponents, managedRunTypeComponents) + managedBootstrapComponents, err := nr.CreateManagedBootstrapComponents( + managedStatusCoreComponents, + managedCoreComponents, + managedCryptoComponents, + managedNetworkComponents, + managedRunTypeComponents, + ) require.Nil(t, err) require.NotNil(t, managedBootstrapComponents) diff --git a/integrationTests/factory/consensusComponents/consensusComponents_test.go b/integrationTests/factory/consensusComponents/consensusComponents_test.go index ef679ce8409..560ba270d61 100644 --- a/integrationTests/factory/consensusComponents/consensusComponents_test.go +++ b/integrationTests/factory/consensusComponents/consensusComponents_test.go @@ -45,9 +45,21 @@ func TestConsensusComponents_Close_ShouldWork(t *testing.T) { require.Nil(t, err) managedNetworkComponents, err := nr.CreateManagedNetworkComponents(managedCoreComponents, managedStatusCoreComponents, managedCryptoComponents) require.Nil(t, err) - managedBootstrapComponents, err := nr.CreateManagedBootstrapComponents(managedStatusCoreComponents, managedCoreComponents, managedCryptoComponents, managedNetworkComponents, managedRunTypeComponents) + managedBootstrapComponents, err := nr.CreateManagedBootstrapComponents( + managedStatusCoreComponents, + managedCoreComponents, + managedCryptoComponents, + managedNetworkComponents, + managedRunTypeComponents, + ) require.Nil(t, err) - managedDataComponents, err := nr.CreateManagedDataComponents(managedStatusCoreComponents, managedCoreComponents, managedBootstrapComponents, managedCryptoComponents, managedRunTypeComponents) + managedDataComponents, err := nr.CreateManagedDataComponents( + managedStatusCoreComponents, + managedCoreComponents, + managedBootstrapComponents, + managedCryptoComponents, + managedRunTypeComponents, + ) require.Nil(t, err) managedStateComponents, err := nr.CreateManagedStateComponents(managedCoreComponents, managedDataComponents, managedStatusCoreComponents, managedRunTypeComponents) require.Nil(t, err) diff --git a/integrationTests/factory/dataComponents/dataComponents_test.go b/integrationTests/factory/dataComponents/dataComponents_test.go index 8a80ef97d55..b24ff0cf8eb 100644 --- a/integrationTests/factory/dataComponents/dataComponents_test.go +++ b/integrationTests/factory/dataComponents/dataComponents_test.go @@ -41,9 +41,21 @@ func TestDataComponents_Create_Close_ShouldWork(t *testing.T) { require.Nil(t, err) managedNetworkComponents, err := nr.CreateManagedNetworkComponents(managedCoreComponents, managedStatusCoreComponents, managedCryptoComponents) require.Nil(t, err) - managedBootstrapComponents, err := nr.CreateManagedBootstrapComponents(managedStatusCoreComponents, managedCoreComponents, managedCryptoComponents, managedNetworkComponents, managedRunTypeComponents) + managedBootstrapComponents, err := nr.CreateManagedBootstrapComponents( + managedStatusCoreComponents, + managedCoreComponents, + managedCryptoComponents, + managedNetworkComponents, + managedRunTypeComponents, + ) require.Nil(t, err) - managedDataComponents, err := nr.CreateManagedDataComponents(managedStatusCoreComponents, managedCoreComponents, managedBootstrapComponents, managedCryptoComponents, managedRunTypeComponents) + managedDataComponents, err := nr.CreateManagedDataComponents( + managedStatusCoreComponents, + managedCoreComponents, + managedBootstrapComponents, + managedCryptoComponents, + managedRunTypeComponents, + ) require.Nil(t, err) require.NotNil(t, managedDataComponents) diff --git a/integrationTests/factory/heartbeatComponents/heartbeatComponents_test.go b/integrationTests/factory/heartbeatComponents/heartbeatComponents_test.go index b978206a18a..62c1cf76551 100644 --- a/integrationTests/factory/heartbeatComponents/heartbeatComponents_test.go +++ b/integrationTests/factory/heartbeatComponents/heartbeatComponents_test.go @@ -45,9 +45,21 @@ func TestHeartbeatComponents_Close_ShouldWork(t *testing.T) { require.Nil(t, err) managedNetworkComponents, err := nr.CreateManagedNetworkComponents(managedCoreComponents, managedStatusCoreComponents, managedCryptoComponents) require.Nil(t, err) - managedBootstrapComponents, err := nr.CreateManagedBootstrapComponents(managedStatusCoreComponents, managedCoreComponents, managedCryptoComponents, managedNetworkComponents, managedRunTypeComponents) + managedBootstrapComponents, err := nr.CreateManagedBootstrapComponents( + managedStatusCoreComponents, + managedCoreComponents, + managedCryptoComponents, + managedNetworkComponents, + managedRunTypeComponents, + ) require.Nil(t, err) - managedDataComponents, err := nr.CreateManagedDataComponents(managedStatusCoreComponents, managedCoreComponents, managedBootstrapComponents, managedCryptoComponents, managedRunTypeComponents) + managedDataComponents, err := nr.CreateManagedDataComponents( + managedStatusCoreComponents, + managedCoreComponents, + managedBootstrapComponents, + managedCryptoComponents, + managedRunTypeComponents, + ) require.Nil(t, err) managedStateComponents, err := nr.CreateManagedStateComponents(managedCoreComponents, managedDataComponents, managedStatusCoreComponents, managedRunTypeComponents) require.Nil(t, err) diff --git a/integrationTests/factory/processComponents/processComponents_test.go b/integrationTests/factory/processComponents/processComponents_test.go index 6069d5b375d..c600dfd9e39 100644 --- a/integrationTests/factory/processComponents/processComponents_test.go +++ b/integrationTests/factory/processComponents/processComponents_test.go @@ -45,9 +45,21 @@ func TestProcessComponents_Close_ShouldWork(t *testing.T) { require.Nil(t, err) managedNetworkComponents, err := nr.CreateManagedNetworkComponents(managedCoreComponents, managedStatusCoreComponents, managedCryptoComponents) require.Nil(t, err) - managedBootstrapComponents, err := nr.CreateManagedBootstrapComponents(managedStatusCoreComponents, managedCoreComponents, managedCryptoComponents, managedNetworkComponents, managedRunTypeComponents) + managedBootstrapComponents, err := nr.CreateManagedBootstrapComponents( + managedStatusCoreComponents, + managedCoreComponents, + managedCryptoComponents, + managedNetworkComponents, + managedRunTypeComponents, + ) require.Nil(t, err) - managedDataComponents, err := nr.CreateManagedDataComponents(managedStatusCoreComponents, managedCoreComponents, managedBootstrapComponents, managedCryptoComponents, managedRunTypeComponents) + managedDataComponents, err := nr.CreateManagedDataComponents( + managedStatusCoreComponents, + managedCoreComponents, + managedBootstrapComponents, + managedCryptoComponents, + managedRunTypeComponents, + ) require.Nil(t, err) managedStateComponents, err := nr.CreateManagedStateComponents(managedCoreComponents, managedDataComponents, managedStatusCoreComponents, managedRunTypeComponents) require.Nil(t, err) diff --git a/integrationTests/factory/stateComponents/stateComponents_test.go b/integrationTests/factory/stateComponents/stateComponents_test.go index 9a54ca4764a..7d51ee6ea6f 100644 --- a/integrationTests/factory/stateComponents/stateComponents_test.go +++ b/integrationTests/factory/stateComponents/stateComponents_test.go @@ -41,9 +41,22 @@ func TestStateComponents_Create_Close_ShouldWork(t *testing.T) { require.Nil(t, err) managedNetworkComponents, err := nr.CreateManagedNetworkComponents(managedCoreComponents, managedStatusCoreComponents, managedCryptoComponents) require.Nil(t, err) - managedBootstrapComponents, err := nr.CreateManagedBootstrapComponents(managedStatusCoreComponents, managedCoreComponents, managedCryptoComponents, managedNetworkComponents, managedRunTypeComponents) - require.Nil(t, err) - managedDataComponents, err := nr.CreateManagedDataComponents(managedStatusCoreComponents, managedCoreComponents, managedBootstrapComponents, managedCryptoComponents, managedRunTypeComponents) + + managedBootstrapComponents, err := nr.CreateManagedBootstrapComponents( + managedStatusCoreComponents, + managedCoreComponents, + managedCryptoComponents, + managedNetworkComponents, + managedRunTypeComponents, + ) + require.Nil(t, err) + managedDataComponents, err := nr.CreateManagedDataComponents( + managedStatusCoreComponents, + managedCoreComponents, + managedBootstrapComponents, + managedCryptoComponents, + managedRunTypeComponents, + ) require.Nil(t, err) managedStateComponents, err := nr.CreateManagedStateComponents(managedCoreComponents, managedDataComponents, managedStatusCoreComponents, managedRunTypeComponents) require.Nil(t, err) diff --git a/integrationTests/factory/statusComponents/statusComponents_test.go b/integrationTests/factory/statusComponents/statusComponents_test.go index f9a7fa7b0ec..9c56284a050 100644 --- a/integrationTests/factory/statusComponents/statusComponents_test.go +++ b/integrationTests/factory/statusComponents/statusComponents_test.go @@ -45,9 +45,21 @@ func TestStatusComponents_Create_Close_ShouldWork(t *testing.T) { require.Nil(t, err) managedNetworkComponents, err := nr.CreateManagedNetworkComponents(managedCoreComponents, managedStatusCoreComponents, managedCryptoComponents) require.Nil(t, err) - managedBootstrapComponents, err := nr.CreateManagedBootstrapComponents(managedStatusCoreComponents, managedCoreComponents, managedCryptoComponents, managedNetworkComponents, managedRunTypeComponents) + managedBootstrapComponents, err := nr.CreateManagedBootstrapComponents( + managedStatusCoreComponents, + managedCoreComponents, + managedCryptoComponents, + managedNetworkComponents, + managedRunTypeComponents, + ) require.Nil(t, err) - managedDataComponents, err := nr.CreateManagedDataComponents(managedStatusCoreComponents, managedCoreComponents, managedBootstrapComponents, managedCryptoComponents, managedRunTypeComponents) + managedDataComponents, err := nr.CreateManagedDataComponents( + managedStatusCoreComponents, + managedCoreComponents, + managedBootstrapComponents, + managedCryptoComponents, + managedRunTypeComponents, + ) require.Nil(t, err) managedStateComponents, err := nr.CreateManagedStateComponents(managedCoreComponents, managedDataComponents, managedStatusCoreComponents, managedRunTypeComponents) require.Nil(t, err) diff --git a/integrationTests/mock/transactionCoordinatorMock.go b/integrationTests/mock/transactionCoordinatorMock.go index 87e913885ae..75a747fe28a 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/integrationTests/multiShard/relayedTx/common.go b/integrationTests/multiShard/relayedTx/common.go index a9098c6c668..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,12 +119,34 @@ func CreateAndSendRelayedAndUserTxV2( _, err := txDispatcherNode.SendTransaction(relayedTx) if err != nil { - fmt.Println(err.Error()) + log.Error("CreateAndSendRelayedAndUserTxV2.SendTransaction", "error", err) } 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 { + log.Error("CreateAndSendRelayedAndUserTxV3.SendTransaction", "error", err) + } + + return relayedTx, relayedTx +} + func createUserTx( player *integrationTests.TestWalletAccount, rcvAddr []byte, @@ -212,6 +236,40 @@ 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) + + txFee := economicsFee.ComputeTxFee(tx) + relayer.Balance.Sub(relayer.Balance, txFee) + + return tx +} + func createAndSendSimpleTransaction( nodes []*integrationTests.TestProcessorNode, player *integrationTests.TestWalletAccount, @@ -225,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/integrationTests/multiShard/relayedTx/relayedTx_test.go b/integrationTests/multiShard/relayedTx/relayedTx_test.go index 41ece5b81eb..c815a5b5eac 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") @@ -188,7 +194,7 @@ func testRelayedTransactionInMultiShardEnvironmentWithSmartContractTX( } time.Sleep(time.Second) - roundToPropagateMultiShard := int64(25) + roundToPropagateMultiShard := int64(40) for i := int64(0); i <= roundToPropagateMultiShard; i++ { round, nonce = integrationTests.ProposeAndSyncOneBlock(t, nodes, idxProposers, round, nonce) integrationTests.AddSelfNotarizedHeaderByMetachain(nodes) @@ -200,7 +206,7 @@ func testRelayedTransactionInMultiShardEnvironmentWithSmartContractTX( finalBalance.Mul(finalBalance, sendValue) checkSCBalance(t, ownerNode, scAddress, receiverAddress1, finalBalance) - checkSCBalance(t, ownerNode, scAddress, receiverAddress1, finalBalance) + checkSCBalance(t, ownerNode, scAddress, receiverAddress2, finalBalance) checkPlayerBalances(t, nodes, players) @@ -436,7 +442,7 @@ func checkSCBalance(t *testing.T, node *integrationTests.TestProcessorNode, scAd }) assert.Nil(t, err) actualBalance := big.NewInt(0).SetBytes(vmOutput.ReturnData[0]) - assert.Equal(t, 0, actualBalance.Cmp(balance)) + assert.Equal(t, balance.String(), actualBalance.String()) } func checkPlayerBalances( diff --git a/integrationTests/realcomponents/processorRunner.go b/integrationTests/realcomponents/processorRunner.go index e28b22b08d5..6a976e0e9e3 100644 --- a/integrationTests/realcomponents/processorRunner.go +++ b/integrationTests/realcomponents/processorRunner.go @@ -77,6 +77,9 @@ func NewProcessorRunner(tb testing.TB, config config.Configs) *ProcessorRunner { } func (pr *ProcessorRunner) createComponents(tb testing.TB) { + var err error + require.Nil(tb, err) + pr.createRunTypeCoreComponents(tb) pr.createCoreComponents(tb) pr.createCryptoComponents(tb) diff --git a/integrationTests/singleShard/block/executingMiniblocks/executingMiniblocks_test.go b/integrationTests/singleShard/block/executingMiniblocks/executingMiniblocks_test.go index 8d6c00af8ae..2c7bb0f7a7c 100644 --- a/integrationTests/singleShard/block/executingMiniblocks/executingMiniblocks_test.go +++ b/integrationTests/singleShard/block/executingMiniblocks/executingMiniblocks_test.go @@ -82,12 +82,11 @@ func TestShardShouldNotProposeAndExecuteTwoBlocksInSameRound(t *testing.T) { } // TestShardShouldProposeBlockContainingInvalidTransactions tests the following scenario: -// 1. generate 3 move balance transactions: one that can be executed, one that can not be executed but the account has -// the balance for the fee and one that is completely invalid (no balance left for it) +// 1. generate 3 move balance transactions: one that can be executed, one to be processed as invalid, and one that isn't executable (no balance left for fee). // 2. proposer will have those 3 transactions in its pools and will propose a block // 3. another node will be able to sync the proposed block (and request - receive) the 2 transactions that // will end up in the block (one valid and one invalid) -// 4. the non-executable transaction will be removed from the proposer's pool +// 4. the non-executable transaction will not be immediately removed from the proposer's pool. See MX-16200. func TestShardShouldProposeBlockContainingInvalidTransactions(t *testing.T) { if testing.Short() { t.Skip("this is not a short test") @@ -195,7 +194,18 @@ func testStateOnNodes(t *testing.T, nodes []*integrationTests.TestProcessorNode, testTxIsInMiniblock(t, proposer, hashes[txValidIdx], block.TxBlock) testTxIsInMiniblock(t, proposer, hashes[txInvalidIdx], block.InvalidBlock) testTxIsInNotInBody(t, proposer, hashes[txDeletedIdx]) - testTxHashNotPresentInPool(t, proposer, hashes[txDeletedIdx]) + + // Removed from mempool. + _, ok := proposer.DataPool.Transactions().SearchFirstData(hashes[txValidIdx]) + assert.False(t, ok) + + // Removed from mempool. + _, ok = proposer.DataPool.Transactions().SearchFirstData(hashes[txInvalidIdx]) + assert.False(t, ok) + + // Not removed from mempool (see MX-16200). + _, ok = proposer.DataPool.Transactions().SearchFirstData(hashes[txDeletedIdx]) + assert.True(t, ok) } func testSameBlockHeight(t *testing.T, nodes []*integrationTests.TestProcessorNode, idxProposer int, expectedHeight uint64) { @@ -208,11 +218,6 @@ func testSameBlockHeight(t *testing.T, nodes []*integrationTests.TestProcessorNo } } -func testTxHashNotPresentInPool(t *testing.T, proposer *integrationTests.TestProcessorNode, hash []byte) { - txCache := proposer.DataPool.Transactions() - _, ok := txCache.SearchFirstData(hash) - assert.False(t, ok) -} func testTxIsInMiniblock(t *testing.T, proposer *integrationTests.TestProcessorNode, hash []byte, bt block.Type) { hdrHandler := proposer.BlockChain.GetCurrentBlockHeader() diff --git a/integrationTests/testProcessorNode.go b/integrationTests/testProcessorNode.go index 7c73741305b..fb67263fae3 100644 --- a/integrationTests/testProcessorNode.go +++ b/integrationTests/testProcessorNode.go @@ -1738,6 +1738,7 @@ func (tpn *TestProcessorNode) initInnerProcessors(gasMap map[string]map[string]u txTypeHandler, _ := coordinator.NewTxTypeHandler(argsTxTypeHandler) tpn.GasHandler, _ = preprocess.NewGasComputation(tpn.EconomicsData, txTypeHandler, tpn.EnableEpochsHandler) badBlocksHandler, _ := tpn.InterimProcContainer.Get(dataBlock.InvalidBlock) + guardianChecker := &guardianMocks.GuardedAccountHandlerStub{} argsNewScProcessor := scrCommon.ArgsNewSmartContractProcessor{ VmContainer: tpn.VMContainer, @@ -1782,7 +1783,7 @@ func (tpn *TestProcessorNode) initInnerProcessors(gasMap map[string]map[string]u ScrForwarder: tpn.ScrForwarder, EnableRoundsHandler: tpn.EnableRoundsHandler, EnableEpochsHandler: tpn.EnableEpochsHandler, - GuardianChecker: &guardianMocks.GuardedAccountHandlerStub{}, + GuardianChecker: guardianChecker, TxVersionChecker: &testscommon.TxVersionCheckerStub{}, TxLogsProcessor: tpn.TransactionLogProcessor, } @@ -2663,22 +2664,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 { @@ -2887,9 +2895,13 @@ func (tpn *TestProcessorNode) WhiteListBody(nodes []*TestProcessorNode, bodyHand } } -// CommitBlock commits the block and body +// CommitBlock commits the block and body. +// This isn't entirely correct, since there's not state rollback if the commit fails. func (tpn *TestProcessorNode) CommitBlock(body data.BodyHandler, header data.HeaderHandler) { - _ = tpn.BlockProcessor.CommitBlock(header, body) + err := tpn.BlockProcessor.CommitBlock(header, body) + if err != nil { + log.Error("TestProcessorNode.CommitBlock", "error", err.Error()) + } } // GetShardHeader returns the first *dataBlock.Header stored in datapools having the nonce provided as parameter diff --git a/integrationTests/vm/esdt/process/esdtProcess_test.go b/integrationTests/vm/esdt/process/esdtProcess_test.go index 8fa9fd04101..4c4b900b3f3 100644 --- a/integrationTests/vm/esdt/process/esdtProcess_test.go +++ b/integrationTests/vm/esdt/process/esdtProcess_test.go @@ -1214,7 +1214,7 @@ func TestScACallsScBWithExecOnDestScAPerformsAsyncCall_NoCallbackInScB(t *testin ) time.Sleep(time.Second) - _, _ = integrationTests.WaitOperationToBeDone(t, nodes, 2, nonce, round, idxProposers) + nonce, round = integrationTests.WaitOperationToBeDone(t, nodes, 2, nonce, round, idxProposers) time.Sleep(time.Second) // issue ESDT by calling exec on dest context on child contract diff --git a/node/chainSimulator/chainSimulator.go b/node/chainSimulator/chainSimulator.go index 9082d3d852a..c7a2ac183bd 100644 --- a/node/chainSimulator/chainSimulator.go +++ b/node/chainSimulator/chainSimulator.go @@ -373,15 +373,18 @@ func (s *simulator) incrementRoundOnAllValidators() { // ForceChangeOfEpoch will force the change of current epoch // This method will call the epoch change trigger and generate block till a new epoch is reached func (s *simulator) ForceChangeOfEpoch() error { + s.mutex.Lock() log.Info("force change of epoch") for shardID, node := range s.nodes { err := node.ForceChangeOfEpoch() if err != nil { + s.mutex.Unlock() return fmt.Errorf("force change of epoch shardID-%d: error-%w", shardID, err) } } epoch := s.nodes[core.MetachainShardId].GetProcessComponents().EpochStartTrigger().Epoch() + s.mutex.Unlock() return s.GenerateBlocksUntilEpochIsReached(int32(epoch + 1)) } diff --git a/node/chainSimulator/components/nodeFacade.go b/node/chainSimulator/components/nodeFacade.go index 9bf2aa10843..4a97b107d1e 100644 --- a/node/chainSimulator/components/nodeFacade.go +++ b/node/chainSimulator/components/nodeFacade.go @@ -180,6 +180,7 @@ func (node *testOnlyProcessingNode) createMetrics(configs config.Configs) error metrics.SaveUint64Metric(node.StatusCoreComponents.AppStatusHandler(), common.MetricMinGasPrice, node.CoreComponentsHolder.EconomicsData().MinGasPrice()) metrics.SaveUint64Metric(node.StatusCoreComponents.AppStatusHandler(), common.MetricMinGasLimit, node.CoreComponentsHolder.EconomicsData().MinGasLimit()) metrics.SaveUint64Metric(node.StatusCoreComponents.AppStatusHandler(), common.MetricExtraGasLimitGuardedTx, node.CoreComponentsHolder.EconomicsData().ExtraGasLimitGuardedTx()) + metrics.SaveUint64Metric(node.StatusCoreComponents.AppStatusHandler(), common.MetricExtraGasLimitRelayedTx, node.CoreComponentsHolder.EconomicsData().MinGasLimit()) metrics.SaveStringMetric(node.StatusCoreComponents.AppStatusHandler(), common.MetricRewardsTopUpGradientPoint, node.CoreComponentsHolder.EconomicsData().RewardsTopUpGradientPoint().String()) metrics.SaveStringMetric(node.StatusCoreComponents.AppStatusHandler(), common.MetricTopUpFactor, fmt.Sprintf("%g", node.CoreComponentsHolder.EconomicsData().RewardsTopUpFactor())) metrics.SaveStringMetric(node.StatusCoreComponents.AppStatusHandler(), common.MetricGasPriceModifier, fmt.Sprintf("%g", node.CoreComponentsHolder.EconomicsData().GasPriceModifier())) diff --git a/node/chainSimulator/components/syncedMessenger.go b/node/chainSimulator/components/syncedMessenger.go index cc437d02038..09786c45842 100644 --- a/node/chainSimulator/components/syncedMessenger.go +++ b/node/chainSimulator/components/syncedMessenger.go @@ -80,13 +80,21 @@ func (messenger *syncedMessenger) receive(fromConnectedPeer core.PeerID, message handlers := messenger.topics[message.Topic()] messenger.mutOperation.RUnlock() + wg := &sync.WaitGroup{} + wg.Add(len(handlers)) for _, handler := range handlers { - err := handler.ProcessReceivedMessage(message, fromConnectedPeer, messenger) - if err != nil { - log.Trace("received message syncedMessenger", - "error", err, "topic", message.Topic(), "from connected peer", fromConnectedPeer.Pretty()) - } + // this is needed to process all received messages on multiple go routines + go func(proc p2p.MessageProcessor, p2pMessage p2p.MessageP2P, peer core.PeerID, localWG *sync.WaitGroup) { + err := proc.ProcessReceivedMessage(p2pMessage, peer, messenger) + if err != nil { + log.Trace("received message syncedMessenger", "error", err, "topic", p2pMessage.Topic(), "from connected peer", peer.Pretty()) + } + + localWG.Done() + }(handler, message, fromConnectedPeer, wg) } + + wg.Wait() } // ProcessReceivedMessage does nothing and returns nil diff --git a/node/chainSimulator/components/testOnlyProcessingNode.go b/node/chainSimulator/components/testOnlyProcessingNode.go index 81d7aa2e5f0..ff120c75cc6 100644 --- a/node/chainSimulator/components/testOnlyProcessingNode.go +++ b/node/chainSimulator/components/testOnlyProcessingNode.go @@ -189,7 +189,13 @@ func NewTestOnlyProcessingNode(args ArgsTestOnlyProcessingNode) (*testOnlyProces return nil, err } - err = instance.createDataPool(args) + instance.DataPool, err = dataRetrieverFactory.NewDataPoolFromConfig(dataRetrieverFactory.ArgsDataPool{ + Config: args.Configs.GeneralConfig, + EconomicsData: instance.CoreComponentsHolder.EconomicsData(), + ShardCoordinator: instance.BootstrapComponentsHolder.ShardCoordinator(), + Marshalizer: instance.CoreComponentsHolder.InternalMarshalizer(), + PathManager: instance.CoreComponentsHolder.PathHandler(), + }) if err != nil { return nil, err } @@ -292,22 +298,6 @@ func (node *testOnlyProcessingNode) createBlockChain(selfShardID uint32) error { return err } -func (node *testOnlyProcessingNode) createDataPool(args ArgsTestOnlyProcessingNode) error { - var err error - - argsDataPool := dataRetrieverFactory.ArgsDataPool{ - Config: args.Configs.GeneralConfig, - EconomicsData: node.CoreComponentsHolder.EconomicsData(), - ShardCoordinator: node.BootstrapComponentsHolder.ShardCoordinator(), - Marshalizer: node.CoreComponentsHolder.InternalMarshalizer(), - PathManager: node.CoreComponentsHolder.PathHandler(), - } - - node.DataPool, err = dataRetrieverFactory.NewDataPoolFromConfig(argsDataPool) - - return err -} - func (node *testOnlyProcessingNode) createNodesCoordinator(pref config.PreferencesConfig, generalConfig config.Config) error { nodesShufflerOut, err := bootstrapComp.CreateNodesShuffleOut( node.CoreComponentsHolder.GenesisNodesSetup(), diff --git a/node/external/blockAPI/baseBlock.go b/node/external/blockAPI/baseBlock.go index 414dc01dcd2..f9b7bbcb780 100644 --- a/node/external/blockAPI/baseBlock.go +++ b/node/external/blockAPI/baseBlock.go @@ -126,6 +126,18 @@ func (bap *baseAPIBlockProcessor) getAndAttachTxsToMb( firstProcessed := mbHeader.GetIndexOfFirstTxProcessed() lastProcessed := mbHeader.GetIndexOfLastTxProcessed() + + // When options.ForHyperblock is true, there are two scenarios: + // 1 - If not all transactions were executed, no transactions will be returned. + // 2 - If all transactions were executed, all transactions starting from index 0 will be returned. + if options.ForHyperblock { + allTxsWereExecuted := lastProcessed == int32(len(miniBlock.TxHashes)-1) + if !allTxsWereExecuted { + return nil + } + firstProcessed = 0 + } + return bap.getAndAttachTxsToMbByEpoch(miniblockHash, miniBlock, header, apiMiniblock, firstProcessed, lastProcessed, options) } 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 0ecb0baa035..767ea3a3611 100644 --- a/node/external/transactionAPI/apiTransactionProcessor.go +++ b/node/external/transactionAPI/apiTransactionProcessor.go @@ -190,7 +190,7 @@ func (atp *apiTransactionProcessor) PopulateComputedFields(tx *transaction.ApiTr } func (atp *apiTransactionProcessor) populateComputedFieldsProcessingType(tx *transaction.ApiTransactionResult) { - typeOnSource, typeOnDestination := atp.txTypeHandler.ComputeTransactionType(tx.Tx) + typeOnSource, typeOnDestination, _ := atp.txTypeHandler.ComputeTransactionType(tx.Tx) tx.ProcessingTypeOnSource = typeOnSource.String() tx.ProcessingTypeOnDestination = typeOnDestination.String() } @@ -206,9 +206,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() } } @@ -401,15 +401,25 @@ 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) - 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()) + + if len(guardedTx.GetGuardianAddr()) > 0 { + 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/apiTransactionProcessor_test.go b/node/external/transactionAPI/apiTransactionProcessor_test.go index f262e9acd72..351990a61db 100644 --- a/node/external/transactionAPI/apiTransactionProcessor_test.go +++ b/node/external/transactionAPI/apiTransactionProcessor_test.go @@ -890,34 +890,37 @@ func TestApiTransactionProcessor_GetTransactionsPoolForSender(t *testing.T) { txHash0, txHash1, txHash2 := []byte("txHash0"), []byte("txHash1"), []byte("txHash2") sender := "alice" - txCacheIntraShard, _ := txcache.NewTxCache(txcache.ConfigSourceMe{ - Name: "test", - NumChunks: 4, - NumBytesPerSenderThreshold: 1_048_576, // 1 MB - CountPerSenderThreshold: math.MaxUint32, - }, &txcachemocks.TxGasHandlerMock{ - MinimumGasMove: 1, - MinimumGasPrice: 1, - GasProcessingDivisor: 1, - }) + txCacheIntraShard, err := txcache.NewTxCache(txcache.ConfigSourceMe{ + Name: "test", + NumChunks: 4, + NumBytesThreshold: 1_048_576, // 1 MB + NumBytesPerSenderThreshold: 1_048_576, // 1 MB + CountThreshold: math.MaxUint32, + CountPerSenderThreshold: math.MaxUint32, + NumItemsToPreemptivelyEvict: 1, + }, txcachemocks.NewMempoolHostMock()) + + require.NoError(t, err) + txCacheIntraShard.AddTx(createTx(txHash2, sender, 3)) txCacheIntraShard.AddTx(createTx(txHash0, sender, 1)) txCacheIntraShard.AddTx(createTx(txHash1, sender, 2)) txHash3, txHash4 := []byte("txHash3"), []byte("txHash4") - txCacheWithMeta, _ := txcache.NewTxCache(txcache.ConfigSourceMe{ - Name: "test-meta", - NumChunks: 4, - NumBytesPerSenderThreshold: 1_048_576, // 1 MB - CountPerSenderThreshold: math.MaxUint32, - }, &txcachemocks.TxGasHandlerMock{ - MinimumGasMove: 1, - MinimumGasPrice: 1, - GasProcessingDivisor: 1, - }) + txCacheWithMeta, err := txcache.NewTxCache(txcache.ConfigSourceMe{ + Name: "test-meta", + NumChunks: 4, + NumBytesThreshold: 1_048_576, // 1 MB + NumBytesPerSenderThreshold: 1_048_576, // 1 MB + CountThreshold: math.MaxUint32, + CountPerSenderThreshold: math.MaxUint32, + NumItemsToPreemptivelyEvict: 1, + }, txcachemocks.NewMempoolHostMock()) txCacheWithMeta.AddTx(createTx(txHash3, sender, 4)) txCacheWithMeta.AddTx(createTx(txHash4, sender, 5)) + require.NoError(t, err) + args := createMockArgAPITransactionProcessor() args.DataPool = &dataRetrieverMock.PoolsHolderStub{ TransactionsCalled: func() dataRetriever.ShardedDataCacherNotifier { @@ -947,6 +950,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) @@ -959,7 +965,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 @@ -978,15 +994,14 @@ func TestApiTransactionProcessor_GetLastPoolNonceForSender(t *testing.T) { sender := "alice" lastNonce := uint64(10) txCacheIntraShard, _ := txcache.NewTxCache(txcache.ConfigSourceMe{ - Name: "test", - NumChunks: 4, - NumBytesPerSenderThreshold: 1_048_576, // 1 MB - CountPerSenderThreshold: math.MaxUint32, - }, &txcachemocks.TxGasHandlerMock{ - MinimumGasMove: 1, - MinimumGasPrice: 1, - GasProcessingDivisor: 1, - }) + Name: "test", + NumChunks: 4, + NumBytesThreshold: 1_048_576, // 1 MB + NumBytesPerSenderThreshold: 1_048_576, // 1 MB + CountThreshold: math.MaxUint32, + CountPerSenderThreshold: math.MaxUint32, + NumItemsToPreemptivelyEvict: 1, + }, txcachemocks.NewMempoolHostMock()) txCacheIntraShard.AddTx(createTx(txHash2, sender, 3)) txCacheIntraShard.AddTx(createTx(txHash0, sender, 1)) txCacheIntraShard.AddTx(createTx(txHash1, sender, 2)) @@ -1030,27 +1045,29 @@ func TestApiTransactionProcessor_GetTransactionsPoolNonceGapsForSender(t *testin txHash1, txHash2, txHash3, txHash4 := []byte("txHash1"), []byte("txHash2"), []byte("txHash3"), []byte("txHash4") sender := "alice" - txCacheIntraShard, _ := txcache.NewTxCache(txcache.ConfigSourceMe{ - Name: "test", - NumChunks: 4, - NumBytesPerSenderThreshold: 1_048_576, // 1 MB - CountPerSenderThreshold: math.MaxUint32, - }, &txcachemocks.TxGasHandlerMock{ - MinimumGasMove: 1, - MinimumGasPrice: 1, - GasProcessingDivisor: 1, - }) + txCacheIntraShard, err := txcache.NewTxCache(txcache.ConfigSourceMe{ + Name: "test", + NumChunks: 4, + NumBytesThreshold: 1_048_576, // 1 MB + NumBytesPerSenderThreshold: 1_048_576, // 1 MB + CountThreshold: math.MaxUint32, + CountPerSenderThreshold: math.MaxUint32, + NumItemsToPreemptivelyEvict: 1, + }, txcachemocks.NewMempoolHostMock()) - txCacheWithMeta, _ := txcache.NewTxCache(txcache.ConfigSourceMe{ - Name: "test-meta", - NumChunks: 4, - NumBytesPerSenderThreshold: 1_048_576, // 1 MB - CountPerSenderThreshold: math.MaxUint32, - }, &txcachemocks.TxGasHandlerMock{ - MinimumGasMove: 1, - MinimumGasPrice: 1, - GasProcessingDivisor: 1, - }) + require.NoError(t, err) + + txCacheWithMeta, err := txcache.NewTxCache(txcache.ConfigSourceMe{ + Name: "test-meta", + NumChunks: 4, + NumBytesThreshold: 1_048_576, // 1 MB + NumBytesPerSenderThreshold: 1_048_576, // 1 MB + CountThreshold: math.MaxUint32, + CountPerSenderThreshold: math.MaxUint32, + NumItemsToPreemptivelyEvict: 1, + }, txcachemocks.NewMempoolHostMock()) + + require.NoError(t, err) accountNonce := uint64(20) // expected nonce gaps: 21-31, 33-33, 36-38 @@ -1318,8 +1335,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") @@ -1330,10 +1347,23 @@ func TestApiTransactionProcessor_GetTransactionPopulatesComputedFields(t *testin require.Equal(t, process.SCDeployment.String(), tx.ProcessingTypeOnDestination) }) + t.Run("ProcessingType (with relayed v3)", func(t *testing.T) { + txTypeHandler.ComputeTransactionTypeCalled = func(data.TransactionHandler) (process.TransactionType, process.TransactionType, bool) { + return process.MoveBalance, process.SCDeployment, true + } + + dataPool.Transactions().AddData([]byte{0, 3}, &transaction.Transaction{Nonce: 7, SndAddr: []byte("alice"), RcvAddr: []byte("bob")}, 42, "1") + tx, err := processor.GetTransaction("0003", true) + + require.Nil(t, err) + require.Equal(t, process.MoveBalance.String(), tx.ProcessingTypeOnSource) + require.Equal(t, process.SCDeployment.String(), tx.ProcessingTypeOnDestination) + }) + t.Run("IsRefund (false)", func(t *testing.T) { scr := &smartContractResult.SmartContractResult{GasLimit: 0, Data: []byte("@ok"), Value: big.NewInt(0)} - dataPool.UnsignedTransactions().AddData([]byte{0, 3}, scr, 42, "foo") - tx, err := processor.GetTransaction("0003", true) + dataPool.UnsignedTransactions().AddData([]byte{0, 4}, scr, 42, "foo") + tx, err := processor.GetTransaction("0004", true) require.Nil(t, err) require.Equal(t, false, tx.IsRefund) @@ -1341,8 +1371,8 @@ func TestApiTransactionProcessor_GetTransactionPopulatesComputedFields(t *testin t.Run("IsRefund (true)", func(t *testing.T) { scr := &smartContractResult.SmartContractResult{GasLimit: 0, Data: []byte("@6f6b"), Value: big.NewInt(500)} - dataPool.UnsignedTransactions().AddData([]byte{0, 4}, scr, 42, "foo") - tx, err := processor.GetTransaction("0004", true) + dataPool.UnsignedTransactions().AddData([]byte{0, 5}, scr, 42, "foo") + tx, err := processor.GetTransaction("0005", true) require.Nil(t, err) require.Equal(t, true, tx.IsRefund) @@ -1362,8 +1392,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/external/transactionAPI/fieldsHandler.go b/node/external/transactionAPI/fieldsHandler.go index 4f837968cb7..f631de38a82 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 = "," @@ -38,8 +40,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..75b3ae6f81a 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), "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), "hashField should have been returned by default") } diff --git a/node/external/transactionAPI/gasUsedAndFeeProcessor.go b/node/external/transactionAPI/gasUsedAndFeeProcessor.go index c4cd9578394..7bbb197c69f 100644 --- a/node/external/transactionAPI/gasUsedAndFeeProcessor.go +++ b/node/external/transactionAPI/gasUsedAndFeeProcessor.go @@ -50,33 +50,49 @@ 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() } + isRelayedV3 := common.IsValidRelayedTxV3(tx.Tx) hasRefundForSender := false + totalRefunds := big.NewInt(0) 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 } - gfp.setGasUsedAndFeeBaseOnRefundValue(tx, userTx, scr.Value) hasRefundForSender = true - break + totalRefunds.Add(totalRefunds, scr.Value) + } + + if totalRefunds.Cmp(big.NewInt(0)) > 0 { + gfp.setGasUsedAndFeeBaseOnRefundValue(tx, userTx, totalRefunds) } 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 } + isRelayedV3 := common.IsValidRelayedTxV3(tx.Tx) + 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/gasUsedAndFeeProcessor_test.go b/node/external/transactionAPI/gasUsedAndFeeProcessor_test.go index 8c5c80826c0..b81ad1e03b9 100644 --- a/node/external/transactionAPI/gasUsedAndFeeProcessor_test.go +++ b/node/external/transactionAPI/gasUsedAndFeeProcessor_test.go @@ -1,6 +1,7 @@ package transactionAPI import ( + "encoding/hex" "math/big" "testing" @@ -257,13 +258,14 @@ func TestNFTTransferWithScCall(t *testing.T) { func TestComputeAndAttachGasUsedAndFeeTransactionWithMultipleScrWithRefund(t *testing.T) { t.Parallel() - feeComp, _ := fee.NewFeeComputer(createEconomicsData(&enableEpochsHandlerMock.EnableEpochsHandlerStub{ + eeh := &enableEpochsHandlerMock.EnableEpochsHandlerStub{ IsFlagEnabledInEpochCalled: func(flag core.EnableEpochFlag, epoch uint32) bool { return flag == common.GasPriceModifierFlag || flag == common.PenalizedTooMuchGasFlag || flag == common.FixRelayedBaseCostFlag }, - })) + } + feeComp, _ := fee.NewFeeComputer(createEconomicsData(eeh)) computer := fee.NewTestFeeComputer(feeComp) gasUsedAndFeeProc := newGasUsedAndFeeProcessor( @@ -271,7 +273,7 @@ func TestComputeAndAttachGasUsedAndFeeTransactionWithMultipleScrWithRefund(t *te pubKeyConverter, &testscommon.ArgumentParserMock{}, &testscommon.MarshallerStub{}, - enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), + eeh, ) txWithSRefundSCR := &transaction.ApiTransactionResult{} @@ -394,3 +396,56 @@ func TestComputeAndAttachGasUsedAndFeeRelayedV1CreateNewDelegationContractWithRe require.Equal(t, "1878500000000000", txWithSRefundSCR.Fee) require.Equal(t, "2177505000000000", txWithSRefundSCR.InitiallyPaidFee) } + +func TestComputeAndAttachGasUsedAndFeeRelayedV3WithMultipleRefunds(t *testing.T) { + t.Parallel() + + eeh := &enableEpochsHandlerMock.EnableEpochsHandlerStub{ + IsFlagEnabledInEpochCalled: func(flag core.EnableEpochFlag, epoch uint32) bool { + return flag == common.GasPriceModifierFlag || + flag == common.PenalizedTooMuchGasFlag || + flag == common.FixRelayedBaseCostFlag || + flag == common.RelayedTransactionsV3Flag + }, + } + feeComp, _ := fee.NewFeeComputer(createEconomicsData(eeh)) + computer := fee.NewTestFeeComputer(feeComp) + + gasUsedAndFeeProc := newGasUsedAndFeeProcessor( + computer, + pubKeyConverter, + &testscommon.ArgumentParserMock{}, + &testscommon.MarshallerStub{}, + eeh, + ) + + txWithRefunds := &transaction.ApiTransactionResult{} + err := core.LoadJsonFile(txWithRefunds, "testData/relayedV3WithMultipleRefunds.json") + require.NoError(t, err) + + txWithRefunds.Fee = "" + txWithRefunds.GasUsed = 0 + + snd, _ := pubKeyConverter.Decode(txWithRefunds.Sender) + rcv, _ := pubKeyConverter.Decode(txWithRefunds.Receiver) + rel, _ := pubKeyConverter.Decode(txWithRefunds.RelayerAddress) + val, _ := big.NewInt(0).SetString(txWithRefunds.Value, 10) + sig, _ := hex.DecodeString(txWithRefunds.Signature) + relayerSig, _ := hex.DecodeString(txWithRefunds.RelayerSignature) + txWithRefunds.Tx = &transaction.Transaction{ + Nonce: txWithRefunds.Nonce, + Value: val, + RcvAddr: rcv, + SndAddr: snd, + RelayerAddr: rel, + GasPrice: txWithRefunds.GasPrice, + GasLimit: txWithRefunds.GasLimit, + Data: txWithRefunds.Data, + Signature: sig, + RelayerSignature: relayerSig, + } + + gasUsedAndFeeProc.computeAndAttachGasUsedAndFee(txWithRefunds) + require.Equal(t, uint64(4220447), txWithRefunds.GasUsed) + require.Equal(t, "289704470000000", txWithRefunds.Fee) +} diff --git a/node/external/transactionAPI/testData/relayedV3WithMultipleRefunds.json b/node/external/transactionAPI/testData/relayedV3WithMultipleRefunds.json new file mode 100644 index 00000000000..26d3005c9d8 --- /dev/null +++ b/node/external/transactionAPI/testData/relayedV3WithMultipleRefunds.json @@ -0,0 +1,159 @@ +{ + "type": "normal", + "processingTypeOnSource": "SCInvoking", + "processingTypeOnDestination": "SCInvoking", + "value": "0", + "receiver": "erd1qqqqqqqqqqqqqpgqak8zt22wl2ph4tswtyc39namqx6ysa2sd8ss4xmlj3", + "sender": "erd1at9keal0jfhamc67ulq4csmchh33eek87yf5hhzcvlw8e5qlx8zq5hjwjl", + "gasPrice": 1000000000, + "gasLimit": 15000000, + "gasUsed": 14536537, + "data": "Zm9yd2FyZEAwMUAwMDAwMDAwMDAwMDAwMDAwMDUwMGMxMzVlMjc2NmM3MDcyMTA2ZjIzYjAzNWIzODUxZDYzZDdmNjIxYzY5NmRhQDAwQDYxNjQ2NDQwMzAzMUAwMDdmZmZmZg==", + "signature": "645d88221a50bbf5173a9a46a70308f0c272d9b4701fe935ae2565147a32b484c4ea695bcaa102a299877f7e7699bb7990c491a601ae636851f5485dd59fe10b", + "smartContractResults": [ + { + "hash": "16b05474872d51840ca9270d8790fc65d50f5d076bb491388344f5095f6f0df0", + "nonce": 24, + "value": 4634630000000, + "receiver": "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", + "sender": "erd1qqqqqqqqqqqqqpgqak8zt22wl2ph4tswtyc39namqx6ysa2sd8ss4xmlj3", + "prevTxHash": "8fe5133b33d315392006d11dbeb2ea9c0d5f1eb0856aaf3044e9f55118656843", + "originalTxHash": "8fe5133b33d315392006d11dbeb2ea9c0d5f1eb0856aaf3044e9f55118656843", + "gasLimit": 0, + "gasPrice": 1000000000, + "callType": 0, + "returnMessage": "gas refund for relayer", + "originalSender": "erd1at9keal0jfhamc67ulq4csmchh33eek87yf5hhzcvlw8e5qlx8zq5hjwjl", + "logs": { + "address": "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", + "events": [ + { + "address": "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", + "identifier": "completedTxEvent", + "topics": [ + "j+UTOzPTFTkgBtEdvrLqnA1fHrCFaq8wROn1URhlaEM=" + ], + "data": null, + "additionalData": null + } + ] + }, + "operation": "transfer", + "isRefund": true + }, + { + "hash": "9090261203cf20e3b51e4c921ce27fa595fcca3f37c83543c7a4a57733139f4d", + "nonce": 1, + "value": 103160900000000, + "receiver": "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", + "sender": "erd1qqqqqqqqqqqqqpgqak8zt22wl2ph4tswtyc39namqx6ysa2sd8ss4xmlj3", + "prevTxHash": "a74d26756c58c522637b3c73b299828901533c93d5ccd75d6ec6a3410755d3e4", + "originalTxHash": "8fe5133b33d315392006d11dbeb2ea9c0d5f1eb0856aaf3044e9f55118656843", + "gasLimit": 0, + "gasPrice": 1000000000, + "callType": 0, + "returnMessage": "gas refund for relayer", + "originalSender": "erd1at9keal0jfhamc67ulq4csmchh33eek87yf5hhzcvlw8e5qlx8zq5hjwjl", + "logs": { + "address": "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", + "events": [ + { + "address": "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", + "identifier": "completedTxEvent", + "topics": [ + "p00mdWxYxSJjezxzspmCiQFTPJPVzNddbsajQQdV0+Q=" + ], + "data": null, + "additionalData": null + } + ] + }, + "operation": "transfer", + "isRefund": true + }, + { + "hash": "5fb1c0d5d04b80331839de417523dff45a1be9489d4d84f9c5535314e0542525", + "nonce": 0, + "value": 0, + "receiver": "erd1qqqqqqqqqqqqqpgqcy67yanvwpepqmerkq6m8pgav0tlvgwxjmdq4hukxw", + "sender": "erd1qqqqqqqqqqqqqpgqak8zt22wl2ph4tswtyc39namqx6ysa2sd8ss4xmlj3", + "relayerAddress": "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", + "relayedValue": 0, + "data": "add@01@21fdaf01f965469c1bebf13c3c49a18f0dde9d8e380cec78cea065246fabb2d9@8fe5133b33d315392006d11dbeb2ea9c0d5f1eb0856aaf3044e9f55118656843@4185d4", + "prevTxHash": "8fe5133b33d315392006d11dbeb2ea9c0d5f1eb0856aaf3044e9f55118656843", + "originalTxHash": "8fe5133b33d315392006d11dbeb2ea9c0d5f1eb0856aaf3044e9f55118656843", + "gasLimit": 12682707, + "gasPrice": 1000000000, + "callType": 1, + "originalSender": "erd1at9keal0jfhamc67ulq4csmchh33eek87yf5hhzcvlw8e5qlx8zq5hjwjl", + "operation": "transfer", + "function": "add" + }, + { + "hash": "a74d26756c58c522637b3c73b299828901533c93d5ccd75d6ec6a3410755d3e4", + "nonce": 0, + "value": 0, + "receiver": "erd1qqqqqqqqqqqqqpgqak8zt22wl2ph4tswtyc39namqx6ysa2sd8ss4xmlj3", + "sender": "erd1qqqqqqqqqqqqqpgqcy67yanvwpepqmerkq6m8pgav0tlvgwxjmdq4hukxw", + "relayerAddress": "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", + "relayedValue": 0, + "data": "@00@95a836710c0857f995e670bf3aa944263473534c8c5f4b7959c938045f62023b@21fdaf01f965469c1bebf13c3c49a18f0dde9d8e380cec78cea065246fabb2d9@8fe5133b33d315392006d11dbeb2ea9c0d5f1eb0856aaf3044e9f55118656843@00", + "prevTxHash": "5fb1c0d5d04b80331839de417523dff45a1be9489d4d84f9c5535314e0542525", + "originalTxHash": "8fe5133b33d315392006d11dbeb2ea9c0d5f1eb0856aaf3044e9f55118656843", + "gasLimit": 11512215, + "gasPrice": 1000000000, + "callType": 2, + "originalSender": "erd1at9keal0jfhamc67ulq4csmchh33eek87yf5hhzcvlw8e5qlx8zq5hjwjl", + "logs": { + "address": "erd1qqqqqqqqqqqqqpgqak8zt22wl2ph4tswtyc39namqx6ysa2sd8ss4xmlj3", + "events": [ + { + "address": "erd1qqqqqqqqqqqqqpgqak8zt22wl2ph4tswtyc39namqx6ysa2sd8ss4xmlj3", + "identifier": "writeLog", + "topics": [ + "AAAAAAAAAAAFAO2OJalO+oN6rg5ZMRLPuwG0SHVQaeE=" + ], + "data": "QDZmNmJAMDBjYTc3YmFjNEAwNjBjY2U1NQ==", + "additionalData": [ + "QDZmNmJAMDBjYTc3YmFjNEAwNjBjY2U1NQ==" + ] + } + ] + }, + "operation": "transfer" + } + ], + "logs": { + "address": "erd1qqqqqqqqqqqqqpgqak8zt22wl2ph4tswtyc39namqx6ysa2sd8ss4xmlj3", + "events": [ + { + "address": "erd1qqqqqqqqqqqqqpgqak8zt22wl2ph4tswtyc39namqx6ysa2sd8ss4xmlj3", + "identifier": "transferValueOnly", + "topics": [ + "", + "AAAAAAAAAAAFAME14nZscHIQbyOwNbOFHWPX9iHGlto=" + ], + "data": "QXN5bmNDYWxs", + "additionalData": [ + "QXN5bmNDYWxs", + "YWRk", + "AQ==" + ] + }, + { + "address": "erd1qqqqqqqqqqqqqpgqak8zt22wl2ph4tswtyc39namqx6ysa2sd8ss4xmlj3", + "identifier": "writeLog", + "topics": [ + "6sts9++Sb93jXufBXEN4veMc5sfxE0vcWGfcfNAfMcQ=" + ], + "data": "QDZmNmI=", + "additionalData": [ + "QDZmNmI=" + ] + } + ] + }, + "isRelayed": true, + "relayerAddress": "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", + "relayerSignature": "dfb60f7d13c024335ec9586b7af579ed47da002a23313272069711bceebddc2d9216b7cc1395c37a1c328288fcc774d5cbeb610db2900f76647307d35a593e08" +} diff --git a/node/external/transactionAPI/unmarshaller.go b/node/external/transactionAPI/unmarshaller.go index dca51592b26..48047afa313 100644 --- a/node/external/transactionAPI/unmarshaller.go +++ b/node/external/transactionAPI/unmarshaller.go @@ -105,7 +105,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 } @@ -136,6 +140,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 } @@ -166,6 +176,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 38c04d2996f..9d71121c25e 100644 --- a/node/node.go +++ b/node/node.go @@ -258,7 +258,7 @@ func (n *Node) baseGetAllIssuedESDTs(tokenType string, ctx context.Context) ([]s continue } - if bytes.Equal(esdtToken.TokenType, []byte(tokenType)) { + if tokenTypeEquals(esdtToken.TokenType, tokenType) { tokens = append(tokens, tokenName) } } @@ -275,6 +275,19 @@ func (n *Node) baseGetAllIssuedESDTs(tokenType string, ctx context.Context) ([]s return tokens, nil } +func tokenTypeEquals(tokenType []byte, providedTokenType string) bool { + if providedTokenType == core.NonFungibleESDTv2 || + providedTokenType == core.NonFungibleESDT { + return bytes.Equal(tokenType, []byte(core.NonFungibleESDTv2)) || bytes.Equal(tokenType, []byte(core.NonFungibleESDT)) || bytes.Equal(tokenType, []byte(core.DynamicNFTESDT)) + } + + if providedTokenType == core.SemiFungibleESDT { + return bytes.Equal(tokenType, []byte(core.SemiFungibleESDT)) || bytes.Equal(tokenType, []byte(core.DynamicSFTESDT)) + } + + return bytes.Equal(tokenType, []byte(providedTokenType)) +} + func (n *Node) getEsdtDataFromLeaf(leaf core.KeyValueHolder) (*systemSmartContracts.ESDTDataV2, bool) { esdtToken := &systemSmartContracts.ESDTDataV2{} @@ -747,7 +760,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.extractAddressFromError(err), ) } @@ -822,6 +835,7 @@ func (n *Node) commonTransactionValidation( enableSignWithTxHash, n.coreComponents.TxSignHasher(), n.coreComponents.TxVersionChecker(), + n.coreComponents.EnableEpochsHandler(), ) if err != nil { return nil, nil, err @@ -870,6 +884,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) @@ -880,6 +897,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 } @@ -936,6 +956,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) @@ -1557,6 +1591,22 @@ func (n *Node) getKeyBytes(key string) ([]byte, error) { return hex.DecodeString(key) } +func (n *Node) extractAddressFromError(err error) string { + if !strings.Contains(err.Error(), "for address") { + return "" + } + + errWords := strings.Split(err.Error(), " ") + for _, word := range errWords { + _, errDecode := n.coreComponents.AddressPubKeyConverter().Decode(word) + if errDecode == nil { + return word + } + } + + return "" +} + // AddClosableComponent adds a closable component to the internal stored components func (n *Node) AddClosableComponent(component mainFactory.Closer) { n.mutClosableComponents.Lock() diff --git a/node/nodeRunner.go b/node/nodeRunner.go index b288d4644b0..6b8e6bc37d4 100644 --- a/node/nodeRunner.go +++ b/node/nodeRunner.go @@ -333,7 +333,13 @@ func (nr *nodeRunner) executeOneComponentCreationCycle( } log.Debug("creating bootstrap components") - managedBootstrapComponents, err := nr.CreateManagedBootstrapComponents(managedStatusCoreComponents, managedCoreComponents, managedCryptoComponents, managedNetworkComponents, managedRunTypeComponents) + managedBootstrapComponents, err := nr.CreateManagedBootstrapComponents( + managedStatusCoreComponents, + managedCoreComponents, + managedCryptoComponents, + managedNetworkComponents, + managedRunTypeComponents, + ) if err != nil { return true, err } @@ -341,7 +347,13 @@ func (nr *nodeRunner) executeOneComponentCreationCycle( nr.logInformation(managedCoreComponents, managedCryptoComponents, managedBootstrapComponents) log.Debug("creating data components") - managedDataComponents, err := nr.CreateManagedDataComponents(managedStatusCoreComponents, managedCoreComponents, managedBootstrapComponents, managedCryptoComponents, managedRunTypeComponents) + managedDataComponents, err := nr.CreateManagedDataComponents( + managedStatusCoreComponents, + managedCoreComponents, + managedBootstrapComponents, + managedCryptoComponents, + managedRunTypeComponents, + ) if err != nil { return true, err } @@ -854,6 +866,7 @@ func (nr *nodeRunner) createMetrics( metrics.SaveUint64Metric(statusCoreComponents.AppStatusHandler(), common.MetricMinGasPrice, coreComponents.EconomicsData().MinGasPrice()) metrics.SaveUint64Metric(statusCoreComponents.AppStatusHandler(), common.MetricMinGasLimit, coreComponents.EconomicsData().MinGasLimit()) metrics.SaveUint64Metric(statusCoreComponents.AppStatusHandler(), common.MetricExtraGasLimitGuardedTx, coreComponents.EconomicsData().ExtraGasLimitGuardedTx()) + metrics.SaveUint64Metric(statusCoreComponents.AppStatusHandler(), common.MetricExtraGasLimitRelayedTx, coreComponents.EconomicsData().MinGasLimit()) metrics.SaveStringMetric(statusCoreComponents.AppStatusHandler(), common.MetricRewardsTopUpGradientPoint, coreComponents.EconomicsData().RewardsTopUpGradientPoint().String()) metrics.SaveStringMetric(statusCoreComponents.AppStatusHandler(), common.MetricTopUpFactor, fmt.Sprintf("%g", coreComponents.EconomicsData().RewardsTopUpFactor())) metrics.SaveStringMetric(statusCoreComponents.AppStatusHandler(), common.MetricGasPriceModifier, fmt.Sprintf("%g", coreComponents.EconomicsData().GasPriceModifier())) @@ -1115,6 +1128,7 @@ func (nr *nodeRunner) CreateManagedStatusComponents( StatusCoreComponents: managedStatusCoreComponents, CryptoComponents: cryptoComponents, IsSovereign: false, + ESDTPrefix: nr.configs.SystemSCConfig.ESDTSystemSCConfig.ESDTPrefix, } statusComponentsFactory, err := statusComp.NewStatusComponentsFactory(statArgs) diff --git a/node/node_test.go b/node/node_test.go index 79e92d56975..6d517986dbf 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -1238,7 +1238,10 @@ func testNodeGetAllIssuedESDTs(t *testing.T, nodeFactory node.NodeFactory, shard acc := createAcc([]byte("newaddress")) esdtToken := []byte("TCK-RANDOM") sftToken := []byte("SFT-RANDOM") + sftTokenDynamic := []byte("SFT-Dynamic") nftToken := []byte("NFT-RANDOM") + nftTokenV2 := []byte("NFT-RANDOM-V2") + nftTokenDynamic := []byte("NFT-Dynamic") esdtData := &systemSmartContracts.ESDTDataV2{TokenName: []byte("fungible"), TokenType: []byte(core.FungibleESDT)} marshalledData, _ := getMarshalizer().Marshal(esdtData) @@ -1248,13 +1251,28 @@ func testNodeGetAllIssuedESDTs(t *testing.T, nodeFactory node.NodeFactory, shard sftMarshalledData, _ := getMarshalizer().Marshal(sftData) _ = acc.SaveKeyValue(sftToken, sftMarshalledData) + sftDataDynamic := &systemSmartContracts.ESDTDataV2{TokenName: []byte("semi fungible dynamic"), TokenType: []byte(core.DynamicSFTESDT)} + sftMarshalledDataDynamic, _ := getMarshalizer().Marshal(sftDataDynamic) + _ = acc.SaveKeyValue(sftTokenDynamic, sftMarshalledDataDynamic) + nftData := &systemSmartContracts.ESDTDataV2{TokenName: []byte("non fungible"), TokenType: []byte(core.NonFungibleESDT)} nftMarshalledData, _ := getMarshalizer().Marshal(nftData) _ = acc.SaveKeyValue(nftToken, nftMarshalledData) + nftData2 := &systemSmartContracts.ESDTDataV2{TokenName: []byte("non fungible v2"), TokenType: []byte(core.NonFungibleESDT)} + nftMarshalledData2, _ := getMarshalizer().Marshal(nftData2) + _ = acc.SaveKeyValue(nftTokenV2, nftMarshalledData2) + + nftDataDynamic := &systemSmartContracts.ESDTDataV2{TokenName: []byte("non fungible dynamic"), TokenType: []byte(core.DynamicNFTESDT)} + nftMarshalledDataDyamic, _ := getMarshalizer().Marshal(nftDataDynamic) + _ = acc.SaveKeyValue(nftTokenDynamic, nftMarshalledDataDyamic) + esdtSuffix := append(esdtToken, acc.AddressBytes()...) nftSuffix := append(nftToken, acc.AddressBytes()...) + nftSuffix2 := append(nftTokenV2, acc.AddressBytes()...) + nftDynamicSuffix := append(nftTokenDynamic, acc.AddressBytes()...) sftSuffix := append(sftToken, acc.AddressBytes()...) + sftDynamicSuffix := append(sftTokenDynamic, acc.AddressBytes()...) acc.SetDataTrie( &trieMock.TrieStub{ @@ -1265,9 +1283,16 @@ func testNodeGetAllIssuedESDTs(t *testing.T, nodeFactory node.NodeFactory, shard trieLeaf, _ = tlp.ParseLeaf(sftToken, append(sftMarshalledData, sftSuffix...), core.NotSpecified) leavesChannels.LeavesChan <- trieLeaf + trieLeaf, _ = tlp.ParseLeaf(sftTokenDynamic, append(sftMarshalledDataDynamic, sftDynamicSuffix...), core.NotSpecified) + leavesChannels.LeavesChan <- trieLeaf trieLeaf, _ = tlp.ParseLeaf(nftToken, append(nftMarshalledData, nftSuffix...), core.NotSpecified) leavesChannels.LeavesChan <- trieLeaf + trieLeaf, _ = tlp.ParseLeaf(nftTokenV2, append(nftMarshalledData2, nftSuffix2...), core.NotSpecified) + leavesChannels.LeavesChan <- trieLeaf + trieLeaf, _ = tlp.ParseLeaf(nftTokenDynamic, append(nftMarshalledDataDyamic, nftDynamicSuffix...), core.NotSpecified) + leavesChannels.LeavesChan <- trieLeaf + close(leavesChannels.LeavesChan) leavesChannels.ErrChan.Close() }() @@ -1316,17 +1341,27 @@ func testNodeGetAllIssuedESDTs(t *testing.T, nodeFactory node.NodeFactory, shard value, err = n.GetAllIssuedESDTs(core.SemiFungibleESDT, context.Background()) assert.Nil(t, err) - assert.Equal(t, 1, len(value)) + assert.Equal(t, 2, len(value)) assert.Equal(t, string(sftToken), value[0]) + assert.Equal(t, string(sftTokenDynamic), value[1]) value, err = n.GetAllIssuedESDTs(core.NonFungibleESDT, context.Background()) assert.Nil(t, err) - assert.Equal(t, 1, len(value)) + assert.Equal(t, 3, len(value)) // for both versions + assert.Equal(t, string(nftToken), value[0]) + assert.Equal(t, string(nftTokenV2), value[1]) + assert.Equal(t, string(nftTokenDynamic), value[2]) + + value, err = n.GetAllIssuedESDTs(core.NonFungibleESDTv2, context.Background()) + assert.Nil(t, err) + assert.Equal(t, 3, len(value)) // for both versions assert.Equal(t, string(nftToken), value[0]) + assert.Equal(t, string(nftTokenV2), value[1]) + assert.Equal(t, string(nftTokenDynamic), value[2]) value, err = n.GetAllIssuedESDTs("", context.Background()) assert.Nil(t, err) - assert.Equal(t, 3, len(value)) + assert.Equal(t, 6, len(value)) } func TestNode_GetAllESDTTokens_ShouldWork(t *testing.T) { @@ -3217,15 +3252,63 @@ 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, no funds for sender", 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 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()) + }) } func TestCreateShardedStores_NilShardCoordinatorShouldError(t *testing.T) { @@ -5472,7 +5555,7 @@ func getDefaultCoreComponents() *nodeMockFactory.CoreComponentsMock { StartTime: time.Time{}, EpochChangeNotifier: &epochNotifier.EpochNotifierStub{}, TxVersionCheckHandler: versioning.NewTxVersionChecker(0), - EnableEpochsHandlerField: &enableEpochsHandlerMock.EnableEpochsHandlerStub{}, + EnableEpochsHandlerField: enableEpochsHandlerMock.NewEnableEpochsHandlerStub(common.RelayedTransactionsV3Flag), ChanStopProcess: make(chan endProcess.ArgEndProcess, 1), } } diff --git a/outport/mock/economicsDataMock.go b/outport/mock/economicsDataMock.go index cf9cf4dc848..36da6e9a7d4 100644 --- a/outport/mock/economicsDataMock.go +++ b/outport/mock/economicsDataMock.go @@ -20,6 +20,11 @@ const ( type EconomicsHandlerMock struct { } +// ComputeGasUnitsFromRefundValue - +func (e *EconomicsHandlerMock) ComputeGasUnitsFromRefundValue(_ coreData.TransactionWithFeeHandler, _ *big.Int, _ uint32) uint64 { + return 0 +} + // MaxGasLimitPerBlock - func (e *EconomicsHandlerMock) MaxGasLimitPerBlock(_ uint32) uint64 { return 0 diff --git a/outport/process/alteredaccounts/tokensProcessor.go b/outport/process/alteredaccounts/tokensProcessor.go index 687c543bcdf..672a29fc7af 100644 --- a/outport/process/alteredaccounts/tokensProcessor.go +++ b/outport/process/alteredaccounts/tokensProcessor.go @@ -137,7 +137,7 @@ func (tp *tokensProcessor) processMultiTransferEvent(event data.EventHandler, ma if string(tokenID) == vmcommon.EGLDIdentifier { tp.processNativeEGLDTransferWithMultiTransfer(destinationAddress, markedAlteredAccounts) - return + continue } // process event for the sender address diff --git a/outport/process/alteredaccounts/tokensProcessor_test.go b/outport/process/alteredaccounts/tokensProcessor_test.go index af737a1de94..2ac172b88bf 100644 --- a/outport/process/alteredaccounts/tokensProcessor_test.go +++ b/outport/process/alteredaccounts/tokensProcessor_test.go @@ -97,3 +97,46 @@ func TestTokenProcessorProcessEventMultiTransferV2WithEGLD(t *testing.T) { } require.Equal(t, markedAccount2, markedAccounts["receiver"]) } + +func TestTokenProcessorProcessEventMultiTransferV2WithEGLDAndMoreTokens(t *testing.T) { + t.Parallel() + + tp := newTokensProcessor(&mock.ShardCoordinatorStub{}) + + markedAccounts := make(map[string]*markedAlteredAccount) + tp.processEvent(&transaction.Event{ + Identifier: []byte(core.BuiltInFunctionMultiESDTNFTTransfer), + Address: []byte("addr"), + Topics: [][]byte{[]byte("token1"), big.NewInt(0).Bytes(), []byte("2"), []byte(vmcommon.EGLDIdentifier), big.NewInt(0).Bytes(), []byte("3"), []byte("token2"), big.NewInt(0).Bytes(), []byte("2"), []byte("receiver")}, + }, markedAccounts) + + require.Equal(t, 2, len(markedAccounts)) + markedAccount1 := &markedAlteredAccount{ + tokens: map[string]*markedAlteredAccountToken{ + "token1": { + identifier: "token1", + nonce: 0, + }, + "token2": { + identifier: "token2", + nonce: 0, + }, + }, + } + require.Equal(t, markedAccount1, markedAccounts["addr"]) + + markedAccount2 := &markedAlteredAccount{ + balanceChanged: true, + tokens: map[string]*markedAlteredAccountToken{ + "token1": { + identifier: "token1", + nonce: 0, + }, + "token2": { + identifier: "token2", + nonce: 0, + }, + }, + } + require.Equal(t, markedAccount2, markedAccounts["receiver"]) +} diff --git a/outport/process/interface.go b/outport/process/interface.go index 8c0347be545..374a68559d4 100644 --- a/outport/process/interface.go +++ b/outport/process/interface.go @@ -33,6 +33,7 @@ type GasConsumedProvider interface { // EconomicsDataHandler defines the functionality needed for economics data type EconomicsDataHandler interface { + ComputeGasUnitsFromRefundValue(tx data.TransactionWithFeeHandler, refundValue *big.Int, epoch uint32) uint64 ComputeGasUsedAndFeeBasedOnRefundValue(tx data.TransactionWithFeeHandler, refundValue *big.Int) (uint64, *big.Int) ComputeTxFeeBasedOnGasUsed(tx data.TransactionWithFeeHandler, gasUsed uint64) *big.Int ComputeTxFee(tx data.TransactionWithFeeHandler) *big.Int diff --git a/outport/process/transactionsfee/interface.go b/outport/process/transactionsfee/interface.go index 53042467442..551ee59d1e2 100644 --- a/outport/process/transactionsfee/interface.go +++ b/outport/process/transactionsfee/interface.go @@ -11,6 +11,7 @@ import ( // FeesProcessorHandler defines the interface for the transaction fees processor type FeesProcessorHandler interface { ComputeGasUsedAndFeeBasedOnRefundValue(tx data.TransactionWithFeeHandler, refundValue *big.Int) (uint64, *big.Int) + ComputeGasUnitsFromRefundValue(tx data.TransactionWithFeeHandler, refundValue *big.Int, epoch uint32) uint64 ComputeTxFeeBasedOnGasUsed(tx data.TransactionWithFeeHandler, gasUsed uint64) *big.Int ComputeTxFee(tx data.TransactionWithFeeHandler) *big.Int ComputeGasLimit(tx data.TransactionWithFeeHandler) uint64 diff --git a/outport/process/transactionsfee/transactionChecker.go b/outport/process/transactionsfee/transactionChecker.go index 546fdd9f432..28c2658a9a4 100644 --- a/outport/process/transactionsfee/transactionChecker.go +++ b/outport/process/transactionsfee/transactionChecker.go @@ -9,6 +9,7 @@ import ( "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-core-go/data" "github.com/multiversx/mx-chain-core-go/data/smartContractResult" + "github.com/multiversx/mx-chain-go/common" vmcommon "github.com/multiversx/mx-chain-vm-common-go" ) @@ -47,11 +48,25 @@ func isSCRForSenderWithRefund(scr *smartContractResult.SmartContractResult, txHa } func isRefundForRelayed(dbScResult *smartContractResult.SmartContractResult, tx data.TransactionHandler) bool { + isRelayedV3 := common.IsRelayedTxV3(tx) isForRelayed := string(dbScResult.ReturnMessage) == core.GasRefundForRelayerMessage isForSender := bytes.Equal(dbScResult.RcvAddr, tx.GetSndAddr()) + isForRelayerV3 := isForRelayerOfV3(dbScResult, tx) differentHash := !bytes.Equal(dbScResult.OriginalTxHash, dbScResult.PrevTxHash) - return isForRelayed && isForSender && differentHash + isRefundForRelayedV1V2 := isForRelayed && isForSender && differentHash && !isRelayedV3 + isRefundForRelayedV3 := isForRelayed && isForRelayerV3 && isRelayedV3 + + return isRefundForRelayedV1V2 || isRefundForRelayedV3 +} + +func isForRelayerOfV3(scr *smartContractResult.SmartContractResult, tx data.TransactionHandler) bool { + relayedTx, isRelayedV3 := tx.(data.RelayedTransactionHandler) + if !isRelayedV3 { + return false + } + + return bytes.Equal(relayedTx.GetRelayerAddr(), scr.RcvAddr) } func isDataOk(data []byte) bool { diff --git a/outport/process/transactionsfee/transactionsFeeProcessor.go b/outport/process/transactionsfee/transactionsFeeProcessor.go index 30bcf0fca76..6cbfb0ddbcf 100644 --- a/outport/process/transactionsfee/transactionsFeeProcessor.go +++ b/outport/process/transactionsfee/transactionsFeeProcessor.go @@ -103,7 +103,7 @@ func (tep *transactionsFeeProcessor) PutFeeAndGasUsed(pool *outportcore.Transact txsWithResultsMap := prepareTransactionsAndScrs(pool) tep.prepareNormalTxs(txsWithResultsMap, epoch) - return tep.prepareScrsNoTx(txsWithResultsMap) + return tep.prepareScrsNoTx(txsWithResultsMap, epoch) } func (tep *transactionsFeeProcessor) prepareInvalidTxs(pool *outportcore.TransactionPool) { @@ -136,13 +136,12 @@ func (tep *transactionsFeeProcessor) prepareNormalTxs(transactionsAndScrs *trans feeInfo.SetFee(initialPaidFee) } - userTx, totalFee, isRelayed := tep.getFeeOfRelayed(txWithResult) - isRelayedAfterFix := isRelayed && isFeeFixActive + userTx, totalFee, isRelayedV1V2 := tep.getFeeOfRelayedV1V2(txWithResult) + isRelayedAfterFix := isRelayedV1V2 && isFeeFixActive if isRelayedAfterFix { feeInfo.SetFee(totalFee) feeInfo.SetInitialPaidFee(totalFee) feeInfo.SetGasUsed(big.NewInt(0).Div(totalFee, big.NewInt(0).SetUint64(txHandler.GetGasPrice())).Uint64()) - } tep.prepareTxWithResults(txHashHex, txWithResult, userTx, epoch) @@ -156,6 +155,7 @@ func (tep *transactionsFeeProcessor) prepareTxWithResults( epoch uint32, ) { hasRefund := false + totalRefunds := big.NewInt(0) for _, scrHandler := range txWithResults.scrs { scr, ok := scrHandler.GetTxHandler().(*smartContractResult.SmartContractResult) if !ok { @@ -163,16 +163,24 @@ func (tep *transactionsFeeProcessor) prepareTxWithResults( } if isSCRForSenderWithRefund(scr, txHashHex, txWithResults.GetTxHandler()) || isRefundForRelayed(scr, txWithResults.GetTxHandler()) { - tep.setGasUsedAndFeeBasedOnRefundValue(txWithResults, userTx, scr.Value, epoch) hasRefund = true - break + totalRefunds.Add(totalRefunds, scr.Value) } } + if totalRefunds.Cmp(big.NewInt(0)) > 0 { + tep.setGasUsedAndFeeBasedOnRefundValue(txWithResults, userTx, totalRefunds, epoch) + + } + tep.prepareTxWithResultsBasedOnLogs(txHashHex, txWithResults, userTx, hasRefund, epoch) } -func (tep *transactionsFeeProcessor) getFeeOfRelayed(tx *transactionWithResults) (data.TransactionHandler, *big.Int, bool) { +func (tep *transactionsFeeProcessor) getFeeOfRelayedV1V2(tx *transactionWithResults) (data.TransactionHandler, *big.Int, bool) { + if common.IsValidRelayedTxV3(tx.GetTxHandler()) { + return nil, nil, false + } + if len(tx.GetTxHandler().GetData()) == 0 { return nil, nil, false } @@ -271,7 +279,10 @@ func (tep *transactionsFeeProcessor) setGasUsedAndFeeBasedOnRefundValue( refund *big.Int, epoch uint32, ) { - if !check.IfNil(userTx) && tep.enableEpochsHandler.IsFlagEnabledInEpoch(common.FixRelayedBaseCostFlag, epoch) { + txWithResults.GetFeeInfo().SetHadRefund() + + 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() @@ -289,7 +300,7 @@ func (tep *transactionsFeeProcessor) setGasUsedAndFeeBasedOnRefundValue( txWithResults.GetFeeInfo().SetFee(fee) } -func (tep *transactionsFeeProcessor) prepareScrsNoTx(transactionsAndScrs *transactionsAndScrsHolder) error { +func (tep *transactionsFeeProcessor) prepareScrsNoTx(transactionsAndScrs *transactionsAndScrsHolder, epoch uint32) error { for _, scrHandler := range transactionsAndScrs.scrsNoTx { scr, ok := scrHandler.GetTxHandler().(*smartContractResult.SmartContractResult) if !ok { @@ -311,18 +322,31 @@ func (tep *transactionsFeeProcessor) prepareScrsNoTx(transactionsAndScrs *transa continue } + isRelayedV3 := common.IsValidRelayedTxV3(txFromStorage) isForInitialTxSender := bytes.Equal(scr.RcvAddr, txFromStorage.SndAddr) - if !isForInitialTxSender { + isForRelayerV3 := bytes.Equal(scr.RcvAddr, txFromStorage.RelayerAddr) + shouldSkipRelayedV3 := isRelayedV3 && !isForRelayerV3 + shouldSkipTx := !isRelayedV3 && !isForInitialTxSender || shouldSkipRelayedV3 + if shouldSkipTx { continue } userTx := tep.getUserTxOfRelayed(txFromStorage) if check.IfNil(userTx) { + // relayed v3 and other txs + if isRelayedV3 { + gasUnits := tep.txFeeCalculator.ComputeGasUnitsFromRefundValue(txFromStorage, scr.Value, epoch) + scrHandler.GetFeeInfo().SetGasRefunded(gasUnits) + scrHandler.GetFeeInfo().SetFee(scr.Value) + continue + } + gasUsed, fee := tep.txFeeCalculator.ComputeGasUsedAndFeeBasedOnRefundValue(txFromStorage, scr.Value) scrHandler.GetFeeInfo().SetGasUsed(gasUsed) scrHandler.GetFeeInfo().SetFee(fee) } else { + // relayed v1 and v2 gasUsed, fee := tep.txFeeCalculator.ComputeGasUsedAndFeeBasedOnRefundValue(userTx, scr.Value) gasUsedRelayedTx := tep.txFeeCalculator.ComputeGasLimit(txFromStorage) diff --git a/outport/process/transactionsfee/transactionsFeeProcessor_test.go b/outport/process/transactionsfee/transactionsFeeProcessor_test.go index 6f0e0f94c35..2aa399a26fe 100644 --- a/outport/process/transactionsfee/transactionsFeeProcessor_test.go +++ b/outport/process/transactionsfee/transactionsFeeProcessor_test.go @@ -10,10 +10,13 @@ import ( outportcore "github.com/multiversx/mx-chain-core-go/data/outport" "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/outport/mock" "github.com/multiversx/mx-chain-go/process" + "github.com/multiversx/mx-chain-go/process/economics" "github.com/multiversx/mx-chain-go/testscommon" "github.com/multiversx/mx-chain-go/testscommon/enableEpochsHandlerMock" + "github.com/multiversx/mx-chain-go/testscommon/epochNotifier" "github.com/multiversx/mx-chain-go/testscommon/genericMocks" "github.com/multiversx/mx-chain-go/testscommon/marshallerMock" logger "github.com/multiversx/mx-chain-logger-go" @@ -22,6 +25,18 @@ import ( var pubKeyConverter, _ = pubkeyConverter.NewBech32PubkeyConverter(32, "erd") +func createEconomicsData(enableEpochsHandler common.EnableEpochsHandler) process.EconomicsDataHandler { + economicsConfig := testscommon.GetEconomicsConfig() + economicsData, _ := economics.NewEconomicsData(economics.ArgsNewEconomicsData{ + Economics: &economicsConfig, + EnableEpochsHandler: enableEpochsHandler, + TxVersionChecker: &testscommon.TxVersionCheckerStub{}, + EpochNotifier: &epochNotifier.EpochNotifierStub{}, + }) + + return economicsData +} + func prepareMockArg() ArgTransactionsFeeProcessor { return ArgTransactionsFeeProcessor{ Marshaller: marshallerMock.MarshalizerMock{}, @@ -597,3 +612,66 @@ func TestMoveBalanceWithSignalError(t *testing.T) { require.Nil(t, err) require.Equal(t, uint64(225_500), initialTx.GetFeeInfo().GetGasUsed()) } + +func TestPutFeeAndGasUsedRelayedTxV3(t *testing.T) { + t.Parallel() + + txHash := []byte("relayedTxV3") + scrWithRefund := []byte("scrWithRefund") + refundValueBig, _ := big.NewInt(0).SetString("37105580000000", 10) + initialTx := &outportcore.TxInfo{ + Transaction: &transaction.Transaction{ + Nonce: 9, + SndAddr: []byte("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"), + RcvAddr: []byte("erd1qqqqqqqqqqqqqpgq2nfn5uxjjkjlrzad3jrak8p3p30v79pseddsm73zpw"), + RelayerAddr: []byte("erd1at9keal0jfhamc67ulq4csmchh33eek87yf5hhzcvlw8e5qlx8zq5hjwjl"), + GasLimit: 5000000, + GasPrice: 1000000000, + Data: []byte("add@01"), + Value: big.NewInt(0), + }, + FeeInfo: &outportcore.FeeInfo{Fee: big.NewInt(0)}, + } + + pool := &outportcore.TransactionPool{ + Transactions: map[string]*outportcore.TxInfo{ + hex.EncodeToString(txHash): initialTx, + }, + SmartContractResults: map[string]*outportcore.SCRInfo{ + hex.EncodeToString(scrWithRefund): { + SmartContractResult: &smartContractResult.SmartContractResult{ + Nonce: 10, + GasPrice: 1000000000, + GasLimit: 0, + Value: refundValueBig, + SndAddr: []byte("erd1qqqqqqqqqqqqqpgq2nfn5uxjjkjlrzad3jrak8p3p30v79pseddsm73zpw"), + RcvAddr: []byte("erd1at9keal0jfhamc67ulq4csmchh33eek87yf5hhzcvlw8e5qlx8zq5hjwjl"), + Data: []byte(""), + PrevTxHash: txHash, + OriginalTxHash: txHash, + ReturnMessage: []byte("gas refund for relayer"), + }, + FeeInfo: &outportcore.FeeInfo{ + Fee: big.NewInt(0), + }, + }, + }, + } + + arg := prepareMockArg() + arg.TxFeeCalculator = createEconomicsData(&enableEpochsHandlerMock.EnableEpochsHandlerStub{ + IsFlagEnabledInEpochCalled: func(flag core.EnableEpochFlag, epoch uint32) bool { + return true + }, + }) + txsFeeProc, err := NewTransactionsFeeProcessor(arg) + require.NotNil(t, txsFeeProc) + require.Nil(t, err) + + err = txsFeeProc.PutFeeAndGasUsed(pool, 0) + require.Nil(t, err) + require.Equal(t, big.NewInt(120804420000000), initialTx.GetFeeInfo().GetFee()) + require.Equal(t, uint64(1289442), initialTx.GetFeeInfo().GetGasUsed()) + require.Equal(t, "157910000000000", initialTx.GetFeeInfo().GetInitialPaidFee().String()) + require.True(t, initialTx.GetFeeInfo().HadRefund) +} 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/interfaces.go b/process/block/preprocess/interfaces.go index 4fbf444d5ae..f140bafe524 100644 --- a/process/block/preprocess/interfaces.go +++ b/process/block/preprocess/interfaces.go @@ -2,6 +2,7 @@ package preprocess import ( "math/big" + "time" "github.com/multiversx/mx-chain-go/process" "github.com/multiversx/mx-chain-go/storage/txcache" @@ -9,15 +10,13 @@ import ( // SortedTransactionsProvider defines the public API of the transactions cache type SortedTransactionsProvider interface { - GetSortedTransactions() []*txcache.WrappedTransaction - NotifyAccountNonce(accountKey []byte, nonce uint64) + GetSortedTransactions(session txcache.SelectionSession) []*txcache.WrappedTransaction IsInterfaceNil() bool } // TxCache defines the functionality for the transactions cache type TxCache interface { - SelectTransactionsWithBandwidth(numRequested int, batchSizePerSender int, bandwidthPerSender uint64) []*txcache.WrappedTransaction - NotifyAccountNonce(accountKey []byte, nonce uint64) + SelectTransactions(session txcache.SelectionSession, gasRequested uint64, maxNum int, selectionLoopMaximumDuration time.Duration) ([]*txcache.WrappedTransaction, uint64) IsInterfaceNil() bool } diff --git a/process/block/preprocess/miniBlockBuilder.go b/process/block/preprocess/miniBlockBuilder.go index a1a2e2bc82e..d10e6ba6ee5 100644 --- a/process/block/preprocess/miniBlockBuilder.go +++ b/process/block/preprocess/miniBlockBuilder.go @@ -22,7 +22,6 @@ import ( type miniBlocksBuilderArgs struct { gasTracker gasTracker accounts state.AccountsAdapter - accountTxsShards *accountTxsShards blockSizeComputation BlockSizeComputationHandler balanceComputationHandler BalanceComputationHandler haveTime func() bool @@ -50,7 +49,6 @@ type miniBlockBuilderStats struct { type miniBlocksBuilder struct { gasTracker accounts state.AccountsAdapter - accountTxsShards *accountTxsShards balanceComputationHandler BalanceComputationHandler blockSizeComputation BlockSizeComputationHandler gasConsumedInReceiverShard map[uint32]uint64 @@ -75,7 +73,6 @@ func newMiniBlockBuilder(args miniBlocksBuilderArgs) (*miniBlocksBuilder, error) return &miniBlocksBuilder{ gasTracker: args.gasTracker, accounts: args.accounts, - accountTxsShards: args.accountTxsShards, balanceComputationHandler: args.balanceComputationHandler, blockSizeComputation: args.blockSizeComputation, miniBlocks: initializeMiniBlocksMap(args.gasTracker.shardCoordinator), @@ -117,9 +114,6 @@ func checkMiniBlocksBuilderArgs(args miniBlocksBuilderArgs) error { if check.IfNil(args.txPool) { return process.ErrNilTransactionPool } - if args.accountTxsShards == nil { - return process.ErrNilAccountTxsPerShard - } if args.haveTime == nil { return process.ErrNilHaveTimeHandler } @@ -136,15 +130,6 @@ func checkMiniBlocksBuilderArgs(args miniBlocksBuilderArgs) error { return nil } -func (mbb *miniBlocksBuilder) updateAccountShardsInfo(tx *transaction.Transaction, wtx *txcache.WrappedTransaction) { - mbb.accountTxsShards.Lock() - mbb.accountTxsShards.accountsInfo[string(tx.GetSndAddr())] = &txShardInfo{ - senderShardID: wtx.SenderShardID, - receiverShardID: wtx.ReceiverShardID, - } - mbb.accountTxsShards.Unlock() -} - // checkAddTransaction method returns a set of actions which could be done afterwards, by checking the given transaction func (mbb *miniBlocksBuilder) checkAddTransaction(wtx *txcache.WrappedTransaction) (*processingActions, *transaction.Transaction) { tx, ok := wtx.Tx.(*transaction.Transaction) diff --git a/process/block/preprocess/miniBlockBuilder_test.go b/process/block/preprocess/miniBlockBuilder_test.go index d3a04147864..3ba49aa00a3 100644 --- a/process/block/preprocess/miniBlockBuilder_test.go +++ b/process/block/preprocess/miniBlockBuilder_test.go @@ -4,7 +4,6 @@ import ( "encoding/hex" "errors" "math/big" - "sync" "testing" "github.com/multiversx/mx-chain-core-go/data" @@ -82,16 +81,6 @@ func Test_checkMiniBlocksBuilderArgsNilBlockSizeComputationHandlerShouldErr(t *t require.Equal(t, process.ErrNilBlockSizeComputationHandler, err) } -func Test_checkMiniBlocksBuilderArgsNilAccountsTxsPerShardsShouldErr(t *testing.T) { - t.Parallel() - - args := createDefaultMiniBlockBuilderArgs() - args.accountTxsShards = nil - - err := checkMiniBlocksBuilderArgs(args) - require.Equal(t, process.ErrNilAccountTxsPerShard, err) -} - func Test_checkMiniBlocksBuilderArgsNilBalanceComputationHandlerShouldErr(t *testing.T) { t.Parallel() @@ -151,29 +140,6 @@ func Test_checkMiniBlocksBuilderArgsOK(t *testing.T) { require.Nil(t, err) } -func Test_MiniBlocksBuilderUpdateAccountShardsInfo(t *testing.T) { - t.Parallel() - - args := createDefaultMiniBlockBuilderArgs() - - mbb, _ := newMiniBlockBuilder(args) - senderAddr := []byte("senderAddr") - receiverAddr := []byte("receiverAddr") - tx := createDefaultTx(senderAddr, receiverAddr, 50000) - - senderShardID := uint32(0) - receiverShardID := uint32(0) - wtx := createWrappedTransaction(tx, senderShardID, receiverShardID) - - mbb.updateAccountShardsInfo(tx, wtx) - - addrShardInfo, ok := mbb.accountTxsShards.accountsInfo[string(tx.SndAddr)] - require.True(t, ok) - - require.Equal(t, senderShardID, addrShardInfo.senderShardID) - require.Equal(t, receiverShardID, addrShardInfo.receiverShardID) -} - func Test_MiniBlocksBuilderHandleGasRefundIntraShard(t *testing.T) { t.Parallel() @@ -659,12 +625,11 @@ func Test_MiniBlocksBuilderCheckAddTransactionWrongTypeAssertion(t *testing.T) { t.Parallel() wtx := &txcache.WrappedTransaction{ - Tx: nil, - TxHash: nil, - SenderShardID: 0, - ReceiverShardID: 0, - Size: 0, - TxFeeScoreNormalized: 0, + Tx: nil, + TxHash: nil, + SenderShardID: 0, + ReceiverShardID: 0, + Size: 0, } args := createDefaultMiniBlockBuilderArgs() @@ -882,11 +847,7 @@ func createDefaultMiniBlockBuilderArgs() miniBlocksBuilderArgs { }, }, }, - accounts: &stateMock.AccountsStub{}, - accountTxsShards: &accountTxsShards{ - accountsInfo: make(map[string]*txShardInfo), - RWMutex: sync.RWMutex{}, - }, + accounts: &stateMock.AccountsStub{}, blockSizeComputation: &testscommon.BlockSizeComputationStub{}, balanceComputationHandler: &testscommon.BalanceComputationStub{}, haveTime: haveTimeTrue, @@ -911,12 +872,14 @@ func createWrappedTransaction( txMarshalled, _ := marshaller.Marshal(tx) txHash := hasher.Compute(string(txMarshalled)) - return &txcache.WrappedTransaction{ - Tx: tx, - TxHash: txHash, - SenderShardID: senderShardID, - ReceiverShardID: receiverShardID, - Size: int64(len(txMarshalled)), - TxFeeScoreNormalized: 10, + wrappedTx := &txcache.WrappedTransaction{ + Tx: tx, + TxHash: txHash, + SenderShardID: senderShardID, + ReceiverShardID: receiverShardID, + Size: int64(len(txMarshalled)), } + + wrappedTx.PricePerUnit = 1_000_000_000 + return wrappedTx } diff --git a/process/block/preprocess/selectionSession.go b/process/block/preprocess/selectionSession.go new file mode 100644 index 00000000000..7e3b35687a1 --- /dev/null +++ b/process/block/preprocess/selectionSession.go @@ -0,0 +1,107 @@ +package preprocess + +import ( + "errors" + + "github.com/multiversx/mx-chain-core-go/core/check" + "github.com/multiversx/mx-chain-core-go/data" + "github.com/multiversx/mx-chain-core-go/data/transaction" + "github.com/multiversx/mx-chain-go/process" + "github.com/multiversx/mx-chain-go/state" + "github.com/multiversx/mx-chain-go/storage/txcache" + vmcommon "github.com/multiversx/mx-chain-vm-common-go" +) + +type selectionSession struct { + accountsAdapter state.AccountsAdapter + transactionsProcessor process.TransactionProcessor + + // Cache of accounts, held in the scope of a single selection session. + // Not concurrency-safe, but never accessed concurrently. + ephemeralAccountsCache map[string]vmcommon.AccountHandler +} + +// ArgsSelectionSession holds the arguments for creating a new selection session. +type ArgsSelectionSession struct { + AccountsAdapter state.AccountsAdapter + TransactionsProcessor process.TransactionProcessor +} + +// NewSelectionSession creates a new selection session. +func NewSelectionSession(args ArgsSelectionSession) (*selectionSession, error) { + if check.IfNil(args.AccountsAdapter) { + return nil, process.ErrNilAccountsAdapter + } + if check.IfNil(args.TransactionsProcessor) { + return nil, process.ErrNilTxProcessor + } + + return &selectionSession{ + accountsAdapter: args.AccountsAdapter, + transactionsProcessor: args.TransactionsProcessor, + ephemeralAccountsCache: make(map[string]vmcommon.AccountHandler), + }, nil +} + +// GetAccountState returns the state of an account. +// Will be called by mempool during transaction selection. +func (session *selectionSession) GetAccountState(address []byte) (*txcache.AccountState, error) { + account, err := session.getExistingAccount(address) + if err != nil { + return nil, err + } + + userAccount, ok := account.(state.UserAccountHandler) + if !ok { + return nil, process.ErrWrongTypeAssertion + } + + return &txcache.AccountState{ + Nonce: userAccount.GetNonce(), + Balance: userAccount.GetBalance(), + }, nil +} + +func (session *selectionSession) getExistingAccount(address []byte) (vmcommon.AccountHandler, error) { + account, ok := session.ephemeralAccountsCache[string(address)] + if ok { + return account, nil + } + + account, err := session.accountsAdapter.GetExistingAccount(address) + if err != nil { + return nil, err + } + + session.ephemeralAccountsCache[string(address)] = account + return account, nil +} + +// IsIncorrectlyGuarded checks if a transaction is incorrectly guarded (not executable). +// Will be called by mempool during transaction selection. +func (session *selectionSession) IsIncorrectlyGuarded(tx data.TransactionHandler) bool { + address := tx.GetSndAddr() + account, err := session.getExistingAccount(address) + if err != nil { + return false + } + + userAccount, ok := account.(state.UserAccountHandler) + if !ok { + // On this branch, we are (approximately) mirroring the behavior of "transactionsProcessor.VerifyGuardian()". + return true + } + + txTyped, ok := tx.(*transaction.Transaction) + if !ok { + return false + } + + err = session.transactionsProcessor.VerifyGuardian(txTyped, userAccount) + return errors.Is(err, process.ErrTransactionNotExecutable) +} + +// IsInterfaceNil returns true if there is no value under the interface +func (session *selectionSession) IsInterfaceNil() bool { + return session == nil +} diff --git a/process/block/preprocess/selectionSession_test.go b/process/block/preprocess/selectionSession_test.go new file mode 100644 index 00000000000..939da698cd1 --- /dev/null +++ b/process/block/preprocess/selectionSession_test.go @@ -0,0 +1,171 @@ +package preprocess + +import ( + "bytes" + "fmt" + "testing" + + "github.com/multiversx/mx-chain-core-go/data/transaction" + "github.com/multiversx/mx-chain-go/process" + "github.com/multiversx/mx-chain-go/state" + "github.com/multiversx/mx-chain-go/testscommon" + stateMock "github.com/multiversx/mx-chain-go/testscommon/state" + vmcommon "github.com/multiversx/mx-chain-vm-common-go" + "github.com/stretchr/testify/require" +) + +func TestNewSelectionSession(t *testing.T) { + t.Parallel() + + session, err := NewSelectionSession(ArgsSelectionSession{ + AccountsAdapter: nil, + TransactionsProcessor: &testscommon.TxProcessorStub{}, + }) + require.Nil(t, session) + require.ErrorIs(t, err, process.ErrNilAccountsAdapter) + + session, err = NewSelectionSession(ArgsSelectionSession{ + AccountsAdapter: &stateMock.AccountsStub{}, + TransactionsProcessor: nil, + }) + require.Nil(t, session) + require.ErrorIs(t, err, process.ErrNilTxProcessor) + + session, err = NewSelectionSession(ArgsSelectionSession{ + AccountsAdapter: &stateMock.AccountsStub{}, + TransactionsProcessor: &testscommon.TxProcessorStub{}, + }) + require.NoError(t, err) + require.NotNil(t, session) +} + +func TestSelectionSession_GetAccountState(t *testing.T) { + t.Parallel() + + accounts := &stateMock.AccountsStub{} + processor := &testscommon.TxProcessorStub{} + + accounts.GetExistingAccountCalled = func(address []byte) (vmcommon.AccountHandler, error) { + if bytes.Equal(address, []byte("alice")) { + return &stateMock.UserAccountStub{ + Address: []byte("alice"), + Nonce: 42, + }, nil + } + + if bytes.Equal(address, []byte("bob")) { + return &stateMock.UserAccountStub{ + Address: []byte("bob"), + Nonce: 7, + IsGuardedCalled: func() bool { + return true + }, + }, nil + } + + return nil, fmt.Errorf("account not found: %s", address) + } + + session, err := NewSelectionSession(ArgsSelectionSession{ + AccountsAdapter: accounts, + TransactionsProcessor: processor, + }) + require.NoError(t, err) + require.NotNil(t, session) + + state, err := session.GetAccountState([]byte("alice")) + require.NoError(t, err) + require.Equal(t, uint64(42), state.Nonce) + + state, err = session.GetAccountState([]byte("bob")) + require.NoError(t, err) + require.Equal(t, uint64(7), state.Nonce) + + state, err = session.GetAccountState([]byte("carol")) + require.ErrorContains(t, err, "account not found: carol") + require.Nil(t, state) +} + +func TestSelectionSession_IsIncorrectlyGuarded(t *testing.T) { + t.Parallel() + + accounts := &stateMock.AccountsStub{} + processor := &testscommon.TxProcessorStub{} + + accounts.GetExistingAccountCalled = func(address []byte) (vmcommon.AccountHandler, error) { + if bytes.Equal(address, []byte("bob")) { + return &stateMock.BaseAccountMock{}, nil + } + + return &stateMock.UserAccountStub{}, nil + } + + processor.VerifyGuardianCalled = func(tx *transaction.Transaction, account state.UserAccountHandler) error { + if tx.Nonce == 43 { + return process.ErrTransactionNotExecutable + } + if tx.Nonce == 44 { + return fmt.Errorf("arbitrary processing error") + } + + return nil + } + + session, err := NewSelectionSession(ArgsSelectionSession{ + AccountsAdapter: accounts, + TransactionsProcessor: processor, + }) + require.NoError(t, err) + require.NotNil(t, session) + + isIncorrectlyGuarded := session.IsIncorrectlyGuarded(&transaction.Transaction{Nonce: 42, SndAddr: []byte("alice")}) + require.False(t, isIncorrectlyGuarded) + + isIncorrectlyGuarded = session.IsIncorrectlyGuarded(&transaction.Transaction{Nonce: 43, SndAddr: []byte("alice")}) + require.True(t, isIncorrectlyGuarded) + + isIncorrectlyGuarded = session.IsIncorrectlyGuarded(&transaction.Transaction{Nonce: 44, SndAddr: []byte("alice")}) + require.False(t, isIncorrectlyGuarded) + + isIncorrectlyGuarded = session.IsIncorrectlyGuarded(&transaction.Transaction{Nonce: 45, SndAddr: []byte("bob")}) + require.True(t, isIncorrectlyGuarded) +} + +func TestSelectionSession_ephemeralAccountsCache_IsSharedAmongCalls(t *testing.T) { + t.Parallel() + + accounts := &stateMock.AccountsStub{} + processor := &testscommon.TxProcessorStub{} + + numCallsGetExistingAccount := 0 + + accounts.GetExistingAccountCalled = func(_ []byte) (vmcommon.AccountHandler, error) { + numCallsGetExistingAccount++ + return &stateMock.UserAccountStub{}, nil + } + + session, err := NewSelectionSession(ArgsSelectionSession{ + AccountsAdapter: accounts, + TransactionsProcessor: processor, + }) + require.NoError(t, err) + require.NotNil(t, session) + + _, _ = session.GetAccountState([]byte("alice")) + require.Equal(t, 1, numCallsGetExistingAccount) + + _, _ = session.GetAccountState([]byte("alice")) + require.Equal(t, 1, numCallsGetExistingAccount) + + _ = session.IsIncorrectlyGuarded(&transaction.Transaction{Nonce: 42, SndAddr: []byte("alice")}) + require.Equal(t, 1, numCallsGetExistingAccount) + + _, _ = session.GetAccountState([]byte("bob")) + require.Equal(t, 2, numCallsGetExistingAccount) + + _, _ = session.GetAccountState([]byte("bob")) + require.Equal(t, 2, numCallsGetExistingAccount) + + _ = session.IsIncorrectlyGuarded(&transaction.Transaction{Nonce: 42, SndAddr: []byte("bob")}) + require.Equal(t, 2, numCallsGetExistingAccount) +} diff --git a/process/block/preprocess/sortedTransactionsProvider.go b/process/block/preprocess/sortedTransactionsProvider.go index 8c2613b7aa7..f30cb912892 100644 --- a/process/block/preprocess/sortedTransactionsProvider.go +++ b/process/block/preprocess/sortedTransactionsProvider.go @@ -32,16 +32,11 @@ func newAdapterTxCacheToSortedTransactionsProvider(txCache TxCache) *adapterTxCa } // GetSortedTransactions gets the transactions from the cache -func (adapter *adapterTxCacheToSortedTransactionsProvider) GetSortedTransactions() []*txcache.WrappedTransaction { - txs := adapter.txCache.SelectTransactionsWithBandwidth(process.MaxNumOfTxsToSelect, process.NumTxPerSenderBatchForFillingMiniblock, process.MaxGasBandwidthPerBatchPerSender) +func (adapter *adapterTxCacheToSortedTransactionsProvider) GetSortedTransactions(session txcache.SelectionSession) []*txcache.WrappedTransaction { + txs, _ := adapter.txCache.SelectTransactions(session, process.TxCacheSelectionGasRequested, process.TxCacheSelectionMaxNumTxs, process.TxCacheSelectionLoopMaximumDuration) return txs } -// NotifyAccountNonce notifies the cache about the current nonce of an account -func (adapter *adapterTxCacheToSortedTransactionsProvider) NotifyAccountNonce(accountKey []byte, nonce uint64) { - adapter.txCache.NotifyAccountNonce(accountKey, nonce) -} - // IsInterfaceNil returns true if there is no value under the interface func (adapter *adapterTxCacheToSortedTransactionsProvider) IsInterfaceNil() bool { return adapter == nil @@ -52,14 +47,10 @@ type disabledSortedTransactionsProvider struct { } // GetSortedTransactions returns an empty slice -func (adapter *disabledSortedTransactionsProvider) GetSortedTransactions() []*txcache.WrappedTransaction { +func (adapter *disabledSortedTransactionsProvider) GetSortedTransactions(_ txcache.SelectionSession) []*txcache.WrappedTransaction { return make([]*txcache.WrappedTransaction, 0) } -// NotifyAccountNonce does nothing -func (adapter *disabledSortedTransactionsProvider) NotifyAccountNonce(_ []byte, _ uint64) { -} - // IsInterfaceNil returns true if there is no value under the interface func (adapter *disabledSortedTransactionsProvider) IsInterfaceNil() bool { return adapter == nil diff --git a/process/block/preprocess/sovereignChainTransactions.go b/process/block/preprocess/sovereignChainTransactions.go index aa13dde6c61..b92ff948fe6 100644 --- a/process/block/preprocess/sovereignChainTransactions.go +++ b/process/block/preprocess/sovereignChainTransactions.go @@ -9,6 +9,7 @@ import ( "github.com/multiversx/mx-chain-core-go/data" "github.com/multiversx/mx-chain-core-go/data/block" "github.com/multiversx/mx-chain-core-go/data/transaction" + "github.com/multiversx/mx-chain-go/process" "github.com/multiversx/mx-chain-go/storage/txcache" ) @@ -110,7 +111,14 @@ func (sct *sovereignChainTransactions) computeSortedTxs( } sortedTransactionsProvider := createSortedTransactionsProvider(txShardPool) - sortedTxs := sortedTransactionsProvider.GetSortedTransactions() + session, err := NewSelectionSession(ArgsSelectionSession{ + AccountsAdapter: sct.accounts, + TransactionsProcessor: sct.txProcessor, + }) + if err != nil { + return nil, err + } + sortedTxs := sortedTransactionsProvider.GetSortedTransactions(session) sct.sortTransactionsBySenderAndNonce(sortedTxs, randomness) return sortedTxs, nil diff --git a/process/block/preprocess/sovereignChainTransactions_test.go b/process/block/preprocess/sovereignChainTransactions_test.go index a71e2b3d4dd..1494b4165be 100644 --- a/process/block/preprocess/sovereignChainTransactions_test.go +++ b/process/block/preprocess/sovereignChainTransactions_test.go @@ -4,10 +4,14 @@ import ( "errors" "math/big" "testing" + "time" "github.com/multiversx/mx-chain-core-go/data" "github.com/multiversx/mx-chain-core-go/data/block" "github.com/multiversx/mx-chain-core-go/data/transaction" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/multiversx/mx-chain-go/common" "github.com/multiversx/mx-chain-go/process" state2 "github.com/multiversx/mx-chain-go/state" @@ -17,8 +21,6 @@ import ( "github.com/multiversx/mx-chain-go/testscommon/economicsmocks" "github.com/multiversx/mx-chain-go/testscommon/enableEpochsHandlerMock" "github.com/multiversx/mx-chain-go/testscommon/state" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) func TestTxsPreprocessor_NewSovereignChainTransactionPreprocessorShouldErrNilPreProcessor(t *testing.T) { @@ -144,10 +146,10 @@ func TestTxsPreprocessor_CreateAndProcessMiniBlocksShouldWork(t *testing.T) { args.TxDataPool = &testscommon.ShardedDataStub{ ShardDataStoreCalled: func(id string) (c storage.Cacher) { return &testscommon.TxCacherStub{ - SelectTransactionsWithBandwidthCalled: func(numRequested int, batchSizePerSender int, bandwidthPerSender uint64) []*txcache.WrappedTransaction { + SelectTransactionsCalled: func(_ txcache.SelectionSession, _ uint64, _ int, _ time.Duration) ([]*txcache.WrappedTransaction, uint64) { return []*txcache.WrappedTransaction{ {Tx: &transaction.Transaction{Nonce: 1}}, - } + }, 0 }, } }, @@ -169,10 +171,10 @@ func TestTxsPreprocessor_CreateAndProcessMiniBlocksShouldWork(t *testing.T) { args.TxDataPool = &testscommon.ShardedDataStub{ ShardDataStoreCalled: func(id string) (c storage.Cacher) { return &testscommon.TxCacherStub{ - SelectTransactionsWithBandwidthCalled: func(numRequested int, batchSizePerSender int, bandwidthPerSender uint64) []*txcache.WrappedTransaction { + SelectTransactionsCalled: func(_ txcache.SelectionSession, _ uint64, _ int, _ time.Duration) ([]*txcache.WrappedTransaction, uint64) { return []*txcache.WrappedTransaction{ {Tx: &transaction.Transaction{Nonce: 1}}, - } + }, 0 }, } }, @@ -224,12 +226,12 @@ func TestTxsPreprocessor_CreateAndProcessMiniBlocksShouldWork(t *testing.T) { args.TxDataPool = &testscommon.ShardedDataStub{ ShardDataStoreCalled: func(id string) (c storage.Cacher) { return &testscommon.TxCacherStub{ - SelectTransactionsWithBandwidthCalled: func(numRequested int, batchSizePerSender int, bandwidthPerSender uint64) []*txcache.WrappedTransaction { + SelectTransactionsCalled: func(_ txcache.SelectionSession, _ uint64, _ int, _ time.Duration) ([]*txcache.WrappedTransaction, uint64) { return []*txcache.WrappedTransaction{ {Tx: tx1, TxHash: txHash1}, {Tx: tx2, TxHash: txHash2}, {Tx: tx3, TxHash: txHash3}, - } + }, 0 }, } }, @@ -275,10 +277,10 @@ func TestTxsPreprocessor_ComputeSortedTxsShouldWork(t *testing.T) { args.TxDataPool = &testscommon.ShardedDataStub{ ShardDataStoreCalled: func(id string) (c storage.Cacher) { return &testscommon.TxCacherStub{ - SelectTransactionsWithBandwidthCalled: func(numRequested int, batchSizePerSender int, bandwidthPerSender uint64) []*txcache.WrappedTransaction { + SelectTransactionsCalled: func(_ txcache.SelectionSession, _ uint64, _ int, _ time.Duration) ([]*txcache.WrappedTransaction, uint64) { return []*txcache.WrappedTransaction{ {Tx: tx, TxHash: txHash}, - } + }, 0 }, } }, diff --git a/process/block/preprocess/transactions.go b/process/block/preprocess/transactions.go index 6fa3d0e22ba..6d6d58f2812 100644 --- a/process/block/preprocess/transactions.go +++ b/process/block/preprocess/transactions.go @@ -16,7 +16,6 @@ import ( "github.com/multiversx/mx-chain-core-go/hashing" "github.com/multiversx/mx-chain-core-go/marshal" logger "github.com/multiversx/mx-chain-logger-go" - vmcommon "github.com/multiversx/mx-chain-vm-common-go" "github.com/multiversx/mx-chain-go/common" "github.com/multiversx/mx-chain-go/dataRetriever" @@ -34,15 +33,10 @@ var _ process.PreProcessor = (*transactions)(nil) var log = logger.GetOrCreate("process/block/preprocess") // 200% bandwidth to allow 100% overshooting estimations -const selectionGasBandwidthIncreasePercent = 200 +const selectionGasBandwidthIncreasePercent = 400 // 130% to allow 30% overshooting estimations for scheduled SC calls -const selectionGasBandwidthIncreaseScheduledPercent = 130 - -type accountTxsShards struct { - accountsInfo map[string]*txShardInfo - sync.RWMutex -} +const selectionGasBandwidthIncreaseScheduledPercent = 260 // TODO: increase code coverage with unit test @@ -63,7 +57,6 @@ type transactions struct { mutOrderedTxs sync.RWMutex blockTracker BlockTracker blockType block.Type - accountTxsShards accountTxsShards emptyAddress []byte txTypeHandler process.TxTypeHandler scheduledTxsExecutionHandler process.ScheduledTxsExecutionHandler @@ -205,7 +198,6 @@ func NewTransactionPreprocessor( txs.txsForCurrBlock.txHashAndInfo = make(map[string]*txInfo) txs.orderedTxs = make(map[string][]data.TransactionHandler) txs.orderedTxHashes = make(map[string][][]byte) - txs.accountTxsShards.accountsInfo = make(map[string]*txShardInfo) txs.accntsTracker = newAccountsTracker() txs.emptyAddress = make([]byte, txs.pubkeyConverter.Len()) @@ -734,7 +726,6 @@ func (txs *transactions) createScheduledMiniBlocksFromMeAsValidator( mapSCTxs map[string]struct{}, randomness []byte, ) (block.MiniBlockSlice, error) { - if !txs.enableEpochsHandler.IsFlagEnabled(common.ScheduledMiniBlocksFlag) { return make(block.MiniBlockSlice, 0), nil } @@ -813,10 +804,6 @@ func (txs *transactions) CreateBlockStarted() { txs.orderedTxHashes = make(map[string][][]byte) txs.mutOrderedTxs.Unlock() - txs.accountTxsShards.Lock() - txs.accountTxsShards.accountsInfo = make(map[string]*txShardInfo) - txs.accountTxsShards.Unlock() - txs.accntsTracker.init() txs.scheduledTxsExecutionHandler.Init() } @@ -933,41 +920,6 @@ func (txs *transactions) processAndRemoveBadTransaction( return err } -func (txs *transactions) notifyTransactionProviderIfNeeded() { - txs.accountTxsShards.RLock() - for senderAddress, txShardInfoValue := range txs.accountTxsShards.accountsInfo { - if txShardInfoValue.senderShardID != txs.shardCoordinator.SelfId() { - continue - } - - account, err := txs.getAccountForAddress([]byte(senderAddress)) - if err != nil { - log.Debug("notifyTransactionProviderIfNeeded.getAccountForAddress", "error", err) - continue - } - - strCache := process.ShardCacherIdentifier(txShardInfoValue.senderShardID, txShardInfoValue.receiverShardID) - txShardPool := txs.txPool.ShardDataStore(strCache) - if check.IfNil(txShardPool) { - log.Trace("notifyTransactionProviderIfNeeded", "error", process.ErrNilTxDataPool) - continue - } - - sortedTransactionsProvider := createSortedTransactionsProvider(txShardPool) - sortedTransactionsProvider.NotifyAccountNonce([]byte(senderAddress), account.GetNonce()) - } - txs.accountTxsShards.RUnlock() -} - -func (txs *transactions) getAccountForAddress(address []byte) (vmcommon.AccountHandler, error) { - account, err := txs.accounts.GetExistingAccount(address) - if err != nil { - return nil, err - } - - return account, nil -} - // RequestTransactionsForMiniBlock requests missing transactions for a certain miniblock func (txs *transactions) RequestTransactionsForMiniBlock(miniBlock *block.MiniBlock) int { if miniBlock == nil { @@ -1199,7 +1151,6 @@ func (txs *transactions) createAndProcessMiniBlocksFromMeV1( args := miniBlocksBuilderArgs{ gasTracker: txs.gasTracker, accounts: txs.accounts, - accountTxsShards: &txs.accountTxsShards, balanceComputationHandler: txs.balanceComputation, blockSizeComputation: txs.blockSizeComputation, haveTime: haveTime, @@ -1215,10 +1166,6 @@ func (txs *transactions) createAndProcessMiniBlocksFromMeV1( return nil, nil, err } - defer func() { - go txs.notifyTransactionProviderIfNeeded() - }() - remainingTxs := make([]*txcache.WrappedTransaction, 0) for idx, wtx := range sortedTxs { actions, tx := mbBuilder.checkAddTransaction(wtx) @@ -1302,7 +1249,6 @@ func (txs *transactions) processMiniBlockBuilderTx( ) elapsedTime := time.Since(startTime) mb.stats.totalProcessingTime += elapsedTime - mb.updateAccountShardsInfo(tx, wtx) if err != nil && !errors.Is(err, process.ErrFailedTransaction) { txs.handleBadTransaction(err, wtx, tx, mb, snapshot) @@ -1480,7 +1426,16 @@ func (txs *transactions) computeSortedTxs( sortedTransactionsProvider := createSortedTransactionsProvider(txShardPool) log.Debug("computeSortedTxs.GetSortedTransactions") - sortedTxs := sortedTransactionsProvider.GetSortedTransactions() + + session, err := NewSelectionSession(ArgsSelectionSession{ + AccountsAdapter: txs.accounts, + TransactionsProcessor: txs.txProcessor, + }) + if err != nil { + return nil, nil, err + } + + sortedTxs := sortedTransactionsProvider.GetSortedTransactions(session) // TODO: this could be moved to SortedTransactionsProvider selectedTxs, remainingTxs := txs.preFilterTransactionsWithMoveBalancePriority(sortedTxs, gasBandwidth) diff --git a/process/block/preprocess/transactionsV2.go b/process/block/preprocess/transactionsV2.go index de492b48659..d80e4060895 100644 --- a/process/block/preprocess/transactionsV2.go +++ b/process/block/preprocess/transactionsV2.go @@ -25,10 +25,6 @@ func (txs *transactions) createAndProcessMiniBlocksFromMeV2( log.Debug("createAndProcessMiniBlocksFromMeV2", "totalGasConsumedInSelfShard", mbInfo.gasInfo.totalGasConsumedInSelfShard) - defer func() { - go txs.notifyTransactionProviderIfNeeded() - }() - remainingTxs := make([]*txcache.WrappedTransaction, 0) for index := range sortedTxs { if !haveTime() { @@ -175,10 +171,6 @@ func (txs *transactions) processTransaction( elapsedTime = time.Since(startTime) mbInfo.processingInfo.totalTimeUsedForProcess += elapsedTime - txs.accountTxsShards.Lock() - txs.accountTxsShards.accountsInfo[string(tx.GetSndAddr())] = &txShardInfo{senderShardID: senderShardID, receiverShardID: receiverShardID} - txs.accountTxsShards.Unlock() - if err != nil && !errors.Is(err, process.ErrFailedTransaction) { if errors.Is(err, process.ErrHigherNonceInTransaction) { mbInfo.senderAddressToSkip = tx.GetSndAddr() @@ -379,10 +371,6 @@ func (txs *transactions) verifyTransaction( elapsedTime = time.Since(startTime) mbInfo.schedulingInfo.totalTimeUsedForScheduledVerify += elapsedTime - txs.accountTxsShards.Lock() - txs.accountTxsShards.accountsInfo[string(tx.GetSndAddr())] = &txShardInfo{senderShardID: senderShardID, receiverShardID: receiverShardID} - txs.accountTxsShards.Unlock() - executionErr, canExecute := txs.isTransactionEligibleForExecutionFunc(tx, err) if !canExecute { isTxTargetedForDeletion := errors.Is(executionErr, process.ErrLowerNonceInTransaction) || errors.Is(executionErr, process.ErrInsufficientFee) || errors.Is(executionErr, process.ErrTransactionNotExecutable) @@ -566,7 +554,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) @@ -700,7 +688,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/block/preprocess/transactions_test.go b/process/block/preprocess/transactions_test.go index ae693bd3f40..3329a1308bd 100644 --- a/process/block/preprocess/transactions_test.go +++ b/process/block/preprocess/transactions_test.go @@ -446,6 +446,16 @@ func TestTxsPreprocessor_NewTransactionPreprocessorNilProcessedMiniBlocksTracker assert.Equal(t, process.ErrNilProcessedMiniBlocksTracker, err) } +func TestTxsPreprocessor_NewTransactionPreprocessorNilTxExecutionOrderHandler(t *testing.T) { + t.Parallel() + + args := createDefaultTransactionsProcessorArgs() + args.TxExecutionOrderHandler = nil + txs, err := NewTransactionPreprocessor(args) + assert.Nil(t, txs) + assert.Equal(t, process.ErrNilTxExecutionOrderHandler, err) +} + func TestTxsPreprocessor_NewTransactionPreprocessorOkValsShouldWork(t *testing.T) { t.Parallel() @@ -660,6 +670,14 @@ func TestTransactions_CreateAndProcessMiniBlockCrossShardGasLimitAddAll(t *testi return 0 }, } + args.Accounts = &stateMock.AccountsStub{ + GetExistingAccountCalled: func(_ []byte) (vmcommon.AccountHandler, error) { + return &stateMock.UserAccountStub{ + Nonce: 42, + Balance: big.NewInt(1000000000000000000), + }, nil + }, + } txs, _ := NewTransactionPreprocessor(args) assert.NotNil(t, txs) @@ -670,7 +688,7 @@ func TestTransactions_CreateAndProcessMiniBlockCrossShardGasLimitAddAll(t *testi addedTxs := make([]*transaction.Transaction, 0) for i := 0; i < 10; i++ { - newTx := &transaction.Transaction{GasLimit: uint64(i)} + newTx := &transaction.Transaction{GasLimit: uint64(i), Nonce: 42 + uint64(i)} txHash, _ := core.CalculateHash(args.Marshalizer, args.Hasher, newTx) args.TxDataPool.AddData(txHash, newTx, newTx.Size(), strCache) @@ -714,6 +732,14 @@ func TestTransactions_CreateAndProcessMiniBlockCrossShardGasLimitAddAllAsNoSCCal return 0 }, } + args.Accounts = &stateMock.AccountsStub{ + GetExistingAccountCalled: func(_ []byte) (vmcommon.AccountHandler, error) { + return &stateMock.UserAccountStub{ + Nonce: 42, + Balance: big.NewInt(1000000000000000000), + }, nil + }, + } args.TxDataPool, _ = dataRetrieverMock.CreateTxPool(2, 0) txs, _ := NewTransactionPreprocessor(args) assert.NotNil(t, txs) @@ -726,7 +752,7 @@ func TestTransactions_CreateAndProcessMiniBlockCrossShardGasLimitAddAllAsNoSCCal addedTxs := make([]*transaction.Transaction, 0) for i := 0; i < 10; i++ { - newTx := &transaction.Transaction{GasLimit: gasLimit, GasPrice: uint64(i), RcvAddr: []byte("012345678910")} + newTx := &transaction.Transaction{GasLimit: gasLimit, GasPrice: uint64(i), RcvAddr: []byte("012345678910"), Nonce: 42 + uint64(i)} txHash, _ := core.CalculateHash(args.Marshalizer, args.Hasher, newTx) args.TxDataPool.AddData(txHash, newTx, newTx.Size(), strCache) @@ -779,6 +805,14 @@ func TestTransactions_CreateAndProcessMiniBlockCrossShardGasLimitAddOnly5asSCCal RemoveGasRefundedCalled: func(hashes [][]byte) { }, } + args.Accounts = &stateMock.AccountsStub{ + GetExistingAccountCalled: func(_ []byte) (vmcommon.AccountHandler, error) { + return &stateMock.UserAccountStub{ + Nonce: 42, + Balance: big.NewInt(1000000000000000000), + }, nil + }, + } txs, _ := NewTransactionPreprocessor(args) @@ -790,7 +824,7 @@ func TestTransactions_CreateAndProcessMiniBlockCrossShardGasLimitAddOnly5asSCCal scAddress, _ := hex.DecodeString("000000000000000000005fed9c659422cd8429ce92f8973bba2a9fb51e0eb3a1") for i := 0; i < 10; i++ { - newTx := &transaction.Transaction{GasLimit: gasLimit, GasPrice: uint64(i), RcvAddr: scAddress} + newTx := &transaction.Transaction{GasLimit: gasLimit, GasPrice: uint64(i), RcvAddr: scAddress, Nonce: 42 + uint64(i)} txHash, _ := core.CalculateHash(args.Marshalizer, args.Hasher, newTx) args.TxDataPool.AddData(txHash, newTx, newTx.Size(), strCache) diff --git a/process/block/shardblock_test.go b/process/block/shardblock_test.go index a4daf5abb6c..11d0938f982 100644 --- a/process/block/shardblock_test.go +++ b/process/block/shardblock_test.go @@ -3065,6 +3065,12 @@ func TestShardProcessor_CreateMiniBlocksShouldWorkWithIntraShardTxs(t *testing.T JournalLenCalled: func() int { return 0 }, + GetExistingAccountCalled: func(_ []byte) (vmcommon.AccountHandler, error) { + return &stateMock.UserAccountStub{ + Nonce: 45, + Balance: big.NewInt(1000000000000000000), + }, nil + }, } totalGasProvided := uint64(0) diff --git a/process/constants.go b/process/constants.go index f75e7b882ee..997e0a2a458 100644 --- a/process/constants.go +++ b/process/constants.go @@ -2,6 +2,7 @@ package process import ( "fmt" + "time" ) // BlockHeaderState specifies which is the state of the block header received @@ -79,11 +80,6 @@ const EpochChangeGracePeriod = 1 // in one round, when node processes a received block const MaxHeaderRequestsAllowed = 20 -// NumTxPerSenderBatchForFillingMiniblock defines the number of transactions to be drawn -// from the transactions pool, for a specific sender, in a single pass. -// Drawing transactions for a miniblock happens in multiple passes, until "MaxItemsInBlock" are drawn. -const NumTxPerSenderBatchForFillingMiniblock = 10 - // NonceDifferenceWhenSynced defines the difference between probable highest nonce seen from network and node's last // committed block nonce, after which, node is considered himself not synced const NonceDifferenceWhenSynced = 0 @@ -135,12 +131,6 @@ const MaxShardHeadersAllowedInOneMetaBlock = 60 // which would be included in one meta block if they are available const MinShardHeadersFromSameShardInOneMetaBlock = 10 -// MaxNumOfTxsToSelect defines the maximum number of transactions that should be selected from the cache -const MaxNumOfTxsToSelect = 30000 - -// MaxGasBandwidthPerBatchPerSender defines the maximum gas bandwidth that should be selected for a sender per batch from the cache -const MaxGasBandwidthPerBatchPerSender = 5000000 - // MaxHeadersToWhitelistInAdvance defines the maximum number of headers whose miniblocks will be whitelisted in advance const MaxHeadersToWhitelistInAdvance = 300 @@ -148,3 +138,12 @@ const MaxHeadersToWhitelistInAdvance = 300 // the real gas used, after which the transaction will be considered an attack and all the gas will be consumed and // nothing will be refunded to the sender const MaxGasFeeHigherFactorAccepted = 10 + +// TxCacheSelectionGasRequested defines the maximum total gas for transactions that should be selected from the cache. +const TxCacheSelectionGasRequested = 10_000_000_000 + +// TxCacheSelectionMaxNumTxs defines the maximum number of transactions that should be selected from the cache. +const TxCacheSelectionMaxNumTxs = 30_000 + +// TxCacheSelectionLoopMaximumDuration defines the maximum duration for the loop that selects transactions from the cache. +const TxCacheSelectionLoopMaximumDuration = 250 * time.Millisecond diff --git a/process/coordinator/process.go b/process/coordinator/process.go index ca10dcc2c88..369cc6866da 100644 --- a/process/coordinator/process.go +++ b/process/coordinator/process.go @@ -1628,7 +1628,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 099367e8f1c..53270906bc2 100644 --- a/process/coordinator/process_test.go +++ b/process/coordinator/process_test.go @@ -33,6 +33,7 @@ import ( "github.com/multiversx/mx-chain-go/process/factory/shard" shardData "github.com/multiversx/mx-chain-go/process/factory/shard/data" "github.com/multiversx/mx-chain-go/process/mock" + "github.com/multiversx/mx-chain-go/state" "github.com/multiversx/mx-chain-go/storage" "github.com/multiversx/mx-chain-go/storage/database" "github.com/multiversx/mx-chain-go/storage/storageunit" @@ -589,6 +590,7 @@ func createInterimProcessorContainer() process.IntermediateProcessorContainer { func createPreProcessorContainerWithDataPool( dataPool dataRetriever.PoolsHolder, feeHandler process.FeeHandler, + accounts state.AccountsAdapter, ) process.PreProcessorsContainer { totalGasProvided := uint64(0) @@ -600,7 +602,7 @@ func createPreProcessorContainerWithDataPool( Hasher: &hashingMocks.HasherMock{}, DataPool: dataPool, PubkeyConverter: createMockPubkeyConverter(), - Accounts: &stateMock.AccountsStub{}, + Accounts: accounts, RequestHandler: &testscommon.RequestHandlerStub{}, TxProcessor: &testscommon.TxProcessorMock{ ProcessTransactionCalled: func(transaction *transaction.Transaction) (vmcommon.ReturnCode, error) { @@ -660,8 +662,9 @@ func createPreProcessorContainerWithDataPool( EnableEpochsHandler: enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), TxTypeHandler: &testscommon.TxTypeHandlerMock{}, ScheduledTxsExecutionHandler: &testscommon.ScheduledTxsExecutionStub{}, - ProcessedMiniBlocksTracker: &testscommon.ProcessedMiniBlocksTrackerStub{}, TxExecutionOrderHandler: &commonMock.TxExecutionOrderHandlerStub{}, - RunTypeComponents: processMocks.NewRunTypeComponentsStub(), + ProcessedMiniBlocksTracker: &testscommon.ProcessedMiniBlocksTrackerStub{}, + TxExecutionOrderHandler: &commonMock.TxExecutionOrderHandlerStub{}, + RunTypeComponents: processMocks.NewRunTypeComponentsStub(), } preFactory, _ := shard.NewPreProcessorsContainerFactory(args) container, _ := preFactory.Create() @@ -1261,7 +1264,7 @@ func TestTransactionCoordinator_CreateMbsAndProcessTransactionsFromMeNoTime(t *t tdp := initDataPool(txHash) argsTransactionCoordinator := createMockTransactionCoordinatorArguments() argsTransactionCoordinator.MiniBlockPool = tdp.MiniBlocks() - argsTransactionCoordinator.PreProcessors = createPreProcessorContainerWithDataPool(tdp, FeeHandlerMock()) + argsTransactionCoordinator.PreProcessors = createPreProcessorContainerWithDataPool(tdp, FeeHandlerMock(), argsTransactionCoordinator.Accounts) tc, err := NewTransactionCoordinator(argsTransactionCoordinator) assert.Nil(t, err) assert.NotNil(t, tc) @@ -1280,7 +1283,7 @@ func TestTransactionCoordinator_CreateMbsAndProcessTransactionsFromMeNoSpace(t * tdp := initDataPool(txHash) argsTransactionCoordinator := createMockTransactionCoordinatorArguments() argsTransactionCoordinator.MiniBlockPool = tdp.MiniBlocks() - argsTransactionCoordinator.PreProcessors = createPreProcessorContainerWithDataPool(tdp, FeeHandlerMock()) + argsTransactionCoordinator.PreProcessors = createPreProcessorContainerWithDataPool(tdp, FeeHandlerMock(), argsTransactionCoordinator.Accounts) argsTransactionCoordinator.GasHandler = &testscommon.GasHandlerStub{ TotalGasProvidedCalled: func() uint64 { return totalGasProvided @@ -1309,9 +1312,18 @@ func TestTransactionCoordinator_CreateMbsAndProcessTransactionsFromMe(t *testing } argsTransactionCoordinator := createMockTransactionCoordinatorArguments() + argsTransactionCoordinator.Accounts = &stateMock.AccountsStub{ + GetExistingAccountCalled: func(_ []byte) (vmcommon.AccountHandler, error) { + return &stateMock.UserAccountStub{ + Nonce: 42, + Balance: big.NewInt(1000000000000000000), + }, nil + }, + } argsTransactionCoordinator.ShardCoordinator = mock.NewMultiShardsCoordinatorMock(nrShards) argsTransactionCoordinator.MiniBlockPool = tdp.MiniBlocks() - argsTransactionCoordinator.PreProcessors = createPreProcessorContainerWithDataPool(tdp, FeeHandlerMock()) + argsTransactionCoordinator.PreProcessors = createPreProcessorContainerWithDataPool(tdp, FeeHandlerMock(), argsTransactionCoordinator.Accounts) + tc, err := NewTransactionCoordinator(argsTransactionCoordinator) assert.Nil(t, err) assert.NotNil(t, tc) @@ -1324,7 +1336,7 @@ func TestTransactionCoordinator_CreateMbsAndProcessTransactionsFromMe(t *testing hasher := &hashingMocks.HasherMock{} for shId := uint32(0); shId < nrShards; shId++ { strCache := process.ShardCacherIdentifier(0, shId) - newTx := &transaction.Transaction{GasLimit: uint64(shId)} + newTx := &transaction.Transaction{GasLimit: uint64(shId), Nonce: 42 + uint64(shId)} computedTxHash, _ := core.CalculateHash(marshalizer, hasher, newTx) txPool.AddData(computedTxHash, newTx, newTx.Size(), strCache) @@ -1347,9 +1359,17 @@ func TestTransactionCoordinator_CreateMbsAndProcessTransactionsFromMeMultipleMin } argsTransactionCoordinator := createMockTransactionCoordinatorArguments() + argsTransactionCoordinator.Accounts = &stateMock.AccountsStub{ + GetExistingAccountCalled: func(_ []byte) (vmcommon.AccountHandler, error) { + return &stateMock.UserAccountStub{ + Nonce: 0, + Balance: big.NewInt(1000000000000000000), + }, nil + }, + } argsTransactionCoordinator.ShardCoordinator = mock.NewMultiShardsCoordinatorMock(nrShards) argsTransactionCoordinator.MiniBlockPool = tdp.MiniBlocks() - argsTransactionCoordinator.PreProcessors = createPreProcessorContainerWithDataPool(tdp, FeeHandlerMock()) + argsTransactionCoordinator.PreProcessors = createPreProcessorContainerWithDataPool(tdp, FeeHandlerMock(), argsTransactionCoordinator.Accounts) tc, err := NewTransactionCoordinator(argsTransactionCoordinator) assert.Nil(t, err) assert.NotNil(t, tc) @@ -1400,6 +1420,14 @@ func TestTransactionCoordinator_CreateMbsAndProcessTransactionsFromMeMultipleMin } argsTransactionCoordinator := createMockTransactionCoordinatorArguments() + argsTransactionCoordinator.Accounts = &stateMock.AccountsStub{ + GetExistingAccountCalled: func(_ []byte) (vmcommon.AccountHandler, error) { + return &stateMock.UserAccountStub{ + Nonce: 0, + Balance: big.NewInt(1000000000000000000), + }, nil + }, + } argsTransactionCoordinator.ShardCoordinator = mock.NewMultiShardsCoordinatorMock(nrShards) argsTransactionCoordinator.MiniBlockPool = tdp.MiniBlocks() argsTransactionCoordinator.PreProcessors = createPreProcessorContainerWithDataPool( @@ -1417,7 +1445,7 @@ func TestTransactionCoordinator_CreateMbsAndProcessTransactionsFromMeMultipleMin ComputeGasLimitCalled: func(tx data.TransactionWithFeeHandler) uint64 { return gasLimit / uint64(numMiniBlocks) }, - }) + }, argsTransactionCoordinator.Accounts) tc, err := NewTransactionCoordinator(argsTransactionCoordinator) assert.Nil(t, err) assert.NotNil(t, tc) @@ -1463,6 +1491,14 @@ func TestTransactionCoordinator_CompactAndExpandMiniblocksShouldWork(t *testing. } argsTransactionCoordinator := createMockTransactionCoordinatorArguments() + argsTransactionCoordinator.Accounts = &stateMock.AccountsStub{ + GetExistingAccountCalled: func(_ []byte) (vmcommon.AccountHandler, error) { + return &stateMock.UserAccountStub{ + Nonce: 0, + Balance: big.NewInt(1000000000000000000), + }, nil + }, + } argsTransactionCoordinator.ShardCoordinator = mock.NewMultiShardsCoordinatorMock(nrShards) argsTransactionCoordinator.MiniBlockPool = tdp.MiniBlocks() argsTransactionCoordinator.PreProcessors = createPreProcessorContainerWithDataPool( @@ -1480,7 +1516,7 @@ func TestTransactionCoordinator_CompactAndExpandMiniblocksShouldWork(t *testing. ComputeGasLimitCalled: func(tx data.TransactionWithFeeHandler) uint64 { return 0 }, - }) + }, argsTransactionCoordinator.Accounts) tc, err := NewTransactionCoordinator(argsTransactionCoordinator) assert.Nil(t, err) assert.NotNil(t, tc) @@ -1527,9 +1563,17 @@ func TestTransactionCoordinator_GetAllCurrentUsedTxs(t *testing.T) { } argsTransactionCoordinator := createMockTransactionCoordinatorArguments() + argsTransactionCoordinator.Accounts = &stateMock.AccountsStub{ + GetExistingAccountCalled: func(_ []byte) (vmcommon.AccountHandler, error) { + return &stateMock.UserAccountStub{ + Nonce: 42, + Balance: big.NewInt(1000000000000000000), + }, nil + }, + } argsTransactionCoordinator.ShardCoordinator = mock.NewMultiShardsCoordinatorMock(nrShards) argsTransactionCoordinator.MiniBlockPool = tdp.MiniBlocks() - argsTransactionCoordinator.PreProcessors = createPreProcessorContainerWithDataPool(tdp, FeeHandlerMock()) + argsTransactionCoordinator.PreProcessors = createPreProcessorContainerWithDataPool(tdp, FeeHandlerMock(), argsTransactionCoordinator.Accounts) argsTransactionCoordinator.GasHandler = &testscommon.GasHandlerStub{ ComputeGasProvidedByTxCalled: func(txSndShId uint32, txRcvShId uint32, txHandler data.TransactionHandler) (uint64, uint64, error) { return 0, 0, nil @@ -1551,7 +1595,7 @@ func TestTransactionCoordinator_GetAllCurrentUsedTxs(t *testing.T) { hasher := &hashingMocks.HasherMock{} for i := uint32(0); i < nrShards; i++ { strCache := process.ShardCacherIdentifier(0, i) - newTx := &transaction.Transaction{GasLimit: uint64(i)} + newTx := &transaction.Transaction{GasLimit: uint64(i), Nonce: 42 + uint64(i)} computedTxHash, _ := core.CalculateHash(marshalizer, hasher, newTx) txPool.AddData(computedTxHash, newTx, newTx.Size(), strCache) @@ -1572,7 +1616,7 @@ func TestTransactionCoordinator_RequestBlockTransactionsNilBody(t *testing.T) { argsTransactionCoordinator := createMockTransactionCoordinatorArguments() argsTransactionCoordinator.ShardCoordinator = mock.NewMultiShardsCoordinatorMock(nrShards) argsTransactionCoordinator.MiniBlockPool = tdp.MiniBlocks() - argsTransactionCoordinator.PreProcessors = createPreProcessorContainerWithDataPool(tdp, FeeHandlerMock()) + argsTransactionCoordinator.PreProcessors = createPreProcessorContainerWithDataPool(tdp, FeeHandlerMock(), argsTransactionCoordinator.Accounts) tc, err := NewTransactionCoordinator(argsTransactionCoordinator) assert.Nil(t, err) assert.NotNil(t, tc) @@ -1594,7 +1638,7 @@ func TestTransactionCoordinator_RequestBlockTransactionsRequestOne(t *testing.T) argsTransactionCoordinator := createMockTransactionCoordinatorArguments() argsTransactionCoordinator.ShardCoordinator = mock.NewMultiShardsCoordinatorMock(nrShards) argsTransactionCoordinator.MiniBlockPool = tdp.MiniBlocks() - argsTransactionCoordinator.PreProcessors = createPreProcessorContainerWithDataPool(tdp, FeeHandlerMock()) + argsTransactionCoordinator.PreProcessors = createPreProcessorContainerWithDataPool(tdp, FeeHandlerMock(), argsTransactionCoordinator.Accounts) tc, err := NewTransactionCoordinator(argsTransactionCoordinator) assert.Nil(t, err) assert.NotNil(t, tc) @@ -1624,7 +1668,7 @@ func TestTransactionCoordinator_IsDataPreparedForProcessing(t *testing.T) { argsTransactionCoordinator := createMockTransactionCoordinatorArguments() argsTransactionCoordinator.ShardCoordinator = mock.NewMultiShardsCoordinatorMock(nrShards) argsTransactionCoordinator.MiniBlockPool = tdp.MiniBlocks() - argsTransactionCoordinator.PreProcessors = createPreProcessorContainerWithDataPool(tdp, FeeHandlerMock()) + argsTransactionCoordinator.PreProcessors = createPreProcessorContainerWithDataPool(tdp, FeeHandlerMock(), argsTransactionCoordinator.Accounts) tc, err := NewTransactionCoordinator(argsTransactionCoordinator) assert.Nil(t, err) assert.NotNil(t, tc) @@ -1644,7 +1688,7 @@ func TestTransactionCoordinator_SaveTxsToStorage(t *testing.T) { argsTransactionCoordinator.ShardCoordinator = mock.NewMultiShardsCoordinatorMock(3) argsTransactionCoordinator.Accounts = initAccountsMock() argsTransactionCoordinator.MiniBlockPool = tdp.MiniBlocks() - argsTransactionCoordinator.PreProcessors = createPreProcessorContainerWithDataPool(tdp, FeeHandlerMock()) + argsTransactionCoordinator.PreProcessors = createPreProcessorContainerWithDataPool(tdp, FeeHandlerMock(), argsTransactionCoordinator.Accounts) tc, err := NewTransactionCoordinator(argsTransactionCoordinator) assert.Nil(t, err) assert.NotNil(t, tc) @@ -1680,7 +1724,7 @@ func TestTransactionCoordinator_RestoreBlockDataFromStorage(t *testing.T) { argsTransactionCoordinator.ShardCoordinator = mock.NewMultiShardsCoordinatorMock(3) argsTransactionCoordinator.Accounts = initAccountsMock() argsTransactionCoordinator.MiniBlockPool = tdp.MiniBlocks() - argsTransactionCoordinator.PreProcessors = createPreProcessorContainerWithDataPool(tdp, FeeHandlerMock()) + argsTransactionCoordinator.PreProcessors = createPreProcessorContainerWithDataPool(tdp, FeeHandlerMock(), argsTransactionCoordinator.Accounts) tc, err := NewTransactionCoordinator(argsTransactionCoordinator) assert.Nil(t, err) assert.NotNil(t, tc) @@ -1718,7 +1762,7 @@ func TestTransactionCoordinator_RemoveBlockDataFromPool(t *testing.T) { argsTransactionCoordinator.ShardCoordinator = mock.NewMultiShardsCoordinatorMock(3) argsTransactionCoordinator.Accounts = initAccountsMock() argsTransactionCoordinator.MiniBlockPool = dataPool.MiniBlocks() - argsTransactionCoordinator.PreProcessors = createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock()) + argsTransactionCoordinator.PreProcessors = createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock(), argsTransactionCoordinator.Accounts) tc, err := NewTransactionCoordinator(argsTransactionCoordinator) assert.Nil(t, err) assert.NotNil(t, tc) @@ -1828,7 +1872,7 @@ func TestTransactionCoordinator_ProcessBlockTransaction(t *testing.T) { argsTransactionCoordinator.ShardCoordinator = mock.NewMultiShardsCoordinatorMock(3) argsTransactionCoordinator.Accounts = initAccountsMock() argsTransactionCoordinator.MiniBlockPool = dataPool.MiniBlocks() - argsTransactionCoordinator.PreProcessors = createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock()) + argsTransactionCoordinator.PreProcessors = createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock(), argsTransactionCoordinator.Accounts) tc, err := NewTransactionCoordinator(argsTransactionCoordinator) assert.Nil(t, err) assert.NotNil(t, tc) @@ -2390,7 +2434,7 @@ func TestTransactionCoordinator_SaveTxsToStorageCallsSaveIntermediate(t *testing argsTransactionCoordinator.ShardCoordinator = mock.NewMultiShardsCoordinatorMock(3) argsTransactionCoordinator.Accounts = initAccountsMock() argsTransactionCoordinator.MiniBlockPool = tdp.MiniBlocks() - argsTransactionCoordinator.PreProcessors = createPreProcessorContainerWithDataPool(tdp, FeeHandlerMock()) + argsTransactionCoordinator.PreProcessors = createPreProcessorContainerWithDataPool(tdp, FeeHandlerMock(), argsTransactionCoordinator.Accounts) argsTransactionCoordinator.InterProcessors = &mock.InterimProcessorContainerMock{ KeysCalled: func() []block.Type { return []block.Type{block.SmartContractResultBlock} @@ -2428,7 +2472,7 @@ func TestTransactionCoordinator_PreprocessorsHasToBeOrderedRewardsAreLast(t *tes argsTransactionCoordinator.ShardCoordinator = mock.NewMultiShardsCoordinatorMock(3) argsTransactionCoordinator.Accounts = initAccountsMock() argsTransactionCoordinator.MiniBlockPool = dataPool.MiniBlocks() - argsTransactionCoordinator.PreProcessors = createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock()) + argsTransactionCoordinator.PreProcessors = createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock(), argsTransactionCoordinator.Accounts) argsTransactionCoordinator.InterProcessors = createInterimProcessorContainer() tc, err := NewTransactionCoordinator(argsTransactionCoordinator) assert.Nil(t, err) @@ -2612,14 +2656,16 @@ func TestTransactionCoordinator_VerifyCreatedMiniBlocksShouldReturnWhenEpochIsNo t.Parallel() dataPool := initDataPool(txHash) + accounts := initAccountsMock() + txCoordinatorArgs := ArgTransactionCoordinator{ Hasher: &hashingMocks.HasherMock{}, Marshalizer: &mock.MarshalizerMock{}, ShardCoordinator: mock.NewMultiShardsCoordinatorMock(3), - Accounts: initAccountsMock(), + Accounts: accounts, MiniBlockPool: dataPool.MiniBlocks(), RequestHandler: &testscommon.RequestHandlerStub{}, - PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock()), + PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock(), accounts), InterProcessors: createInterimProcessorContainer(), GasHandler: &testscommon.GasHandlerStub{}, FeeHandler: &mock.FeeAccumulatorStub{}, @@ -2657,14 +2703,16 @@ func TestTransactionCoordinator_VerifyCreatedMiniBlocksShouldErrMaxGasLimitPerMi maxGasLimitPerBlock := uint64(1500000000) dataPool := initDataPool(txHash) + accounts := initAccountsMock() + txCoordinatorArgs := ArgTransactionCoordinator{ Hasher: &hashingMocks.HasherMock{}, Marshalizer: &mock.MarshalizerMock{}, ShardCoordinator: mock.NewMultiShardsCoordinatorMock(3), - Accounts: initAccountsMock(), + Accounts: accounts, MiniBlockPool: dataPool.MiniBlocks(), RequestHandler: &testscommon.RequestHandlerStub{}, - PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock()), + PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock(), accounts), InterProcessors: createInterimProcessorContainer(), GasHandler: &testscommon.GasHandlerStub{}, FeeHandler: &mock.FeeAccumulatorStub{}, @@ -2723,14 +2771,16 @@ func TestTransactionCoordinator_VerifyCreatedMiniBlocksShouldErrMaxAccumulatedFe maxGasLimitPerBlock := uint64(1500000000) dataPool := initDataPool(txHash) + accounts := initAccountsMock() + txCoordinatorArgs := ArgTransactionCoordinator{ Hasher: &hashingMocks.HasherMock{}, Marshalizer: &mock.MarshalizerMock{}, ShardCoordinator: mock.NewMultiShardsCoordinatorMock(3), - Accounts: initAccountsMock(), + Accounts: accounts, MiniBlockPool: dataPool.MiniBlocks(), RequestHandler: &testscommon.RequestHandlerStub{}, - PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock()), + PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock(), accounts), InterProcessors: createInterimProcessorContainer(), GasHandler: &testscommon.GasHandlerStub{}, FeeHandler: &mock.FeeAccumulatorStub{}, @@ -2800,14 +2850,16 @@ func TestTransactionCoordinator_VerifyCreatedMiniBlocksShouldErrMaxDeveloperFees maxGasLimitPerBlock := uint64(1500000000) dataPool := initDataPool(txHash) + accounts := initAccountsMock() + txCoordinatorArgs := ArgTransactionCoordinator{ Hasher: &hashingMocks.HasherMock{}, Marshalizer: &mock.MarshalizerMock{}, ShardCoordinator: mock.NewMultiShardsCoordinatorMock(3), - Accounts: initAccountsMock(), + Accounts: accounts, MiniBlockPool: dataPool.MiniBlocks(), RequestHandler: &testscommon.RequestHandlerStub{}, - PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock()), + PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock(), accounts), InterProcessors: createInterimProcessorContainer(), GasHandler: &testscommon.GasHandlerStub{}, FeeHandler: &mock.FeeAccumulatorStub{}, @@ -2877,14 +2929,16 @@ func TestTransactionCoordinator_VerifyCreatedMiniBlocksShouldWork(t *testing.T) maxGasLimitPerBlock := uint64(1500000000) dataPool := initDataPool(txHash) + accounts := initAccountsMock() + txCoordinatorArgs := ArgTransactionCoordinator{ Hasher: &hashingMocks.HasherMock{}, Marshalizer: &mock.MarshalizerMock{}, ShardCoordinator: mock.NewMultiShardsCoordinatorMock(3), - Accounts: initAccountsMock(), + Accounts: accounts, MiniBlockPool: dataPool.MiniBlocks(), RequestHandler: &testscommon.RequestHandlerStub{}, - PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock()), + PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock(), accounts), InterProcessors: createInterimProcessorContainer(), GasHandler: &testscommon.GasHandlerStub{}, FeeHandler: &mock.FeeAccumulatorStub{}, @@ -2953,14 +3007,16 @@ func TestTransactionCoordinator_GetAllTransactionsShouldWork(t *testing.T) { t.Parallel() dataPool := initDataPool(txHash) + accounts := initAccountsMock() + txCoordinatorArgs := ArgTransactionCoordinator{ Hasher: &hashingMocks.HasherMock{}, Marshalizer: &mock.MarshalizerMock{}, ShardCoordinator: mock.NewMultiShardsCoordinatorMock(3), - Accounts: initAccountsMock(), + Accounts: accounts, MiniBlockPool: dataPool.MiniBlocks(), RequestHandler: &testscommon.RequestHandlerStub{}, - PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock()), + PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock(), accounts), InterProcessors: createInterimProcessorContainer(), GasHandler: &testscommon.GasHandlerStub{}, FeeHandler: &mock.FeeAccumulatorStub{}, @@ -3026,14 +3082,16 @@ func TestTransactionCoordinator_VerifyGasLimitShouldErrMaxGasLimitPerMiniBlockIn tx3GasLimit := uint64(300000001) dataPool := initDataPool(txHash) + accounts := initAccountsMock() + txCoordinatorArgs := ArgTransactionCoordinator{ Hasher: &hashingMocks.HasherMock{}, Marshalizer: &mock.MarshalizerMock{}, ShardCoordinator: mock.NewMultiShardsCoordinatorMock(3), - Accounts: initAccountsMock(), + Accounts: accounts, MiniBlockPool: dataPool.MiniBlocks(), RequestHandler: &testscommon.RequestHandlerStub{}, - PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock()), + PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock(), accounts), InterProcessors: createInterimProcessorContainer(), GasHandler: &testscommon.GasHandlerStub{}, FeeHandler: &mock.FeeAccumulatorStub{}, @@ -3119,14 +3177,16 @@ func TestTransactionCoordinator_VerifyGasLimitShouldWork(t *testing.T) { tx3GasLimit := uint64(300) dataPool := initDataPool(txHash) + accounts := initAccountsMock() + txCoordinatorArgs := ArgTransactionCoordinator{ Hasher: &hashingMocks.HasherMock{}, Marshalizer: &mock.MarshalizerMock{}, ShardCoordinator: mock.NewMultiShardsCoordinatorMock(3), - Accounts: initAccountsMock(), + Accounts: accounts, MiniBlockPool: dataPool.MiniBlocks(), RequestHandler: &testscommon.RequestHandlerStub{}, - PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock()), + PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock(), accounts), InterProcessors: createInterimProcessorContainer(), GasHandler: &testscommon.GasHandlerStub{}, FeeHandler: &mock.FeeAccumulatorStub{}, @@ -3208,14 +3268,16 @@ func TestTransactionCoordinator_CheckGasProvidedByMiniBlockInReceiverShardShould t.Parallel() dataPool := initDataPool(txHash) + accounts := initAccountsMock() + txCoordinatorArgs := ArgTransactionCoordinator{ Hasher: &hashingMocks.HasherMock{}, Marshalizer: &mock.MarshalizerMock{}, ShardCoordinator: mock.NewMultiShardsCoordinatorMock(3), - Accounts: initAccountsMock(), + Accounts: accounts, MiniBlockPool: dataPool.MiniBlocks(), RequestHandler: &testscommon.RequestHandlerStub{}, - PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock()), + PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock(), accounts), InterProcessors: createInterimProcessorContainer(), GasHandler: &testscommon.GasHandlerStub{}, FeeHandler: &mock.FeeAccumulatorStub{}, @@ -3250,14 +3312,16 @@ func TestTransactionCoordinator_CheckGasProvidedByMiniBlockInReceiverShardShould tx1GasLimit := uint64(100) dataPool := initDataPool(txHash) + accounts := initAccountsMock() + txCoordinatorArgs := ArgTransactionCoordinator{ Hasher: &hashingMocks.HasherMock{}, Marshalizer: &mock.MarshalizerMock{}, ShardCoordinator: mock.NewMultiShardsCoordinatorMock(3), - Accounts: initAccountsMock(), + Accounts: accounts, MiniBlockPool: dataPool.MiniBlocks(), RequestHandler: &testscommon.RequestHandlerStub{}, - PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock()), + PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock(), accounts), InterProcessors: createInterimProcessorContainer(), GasHandler: &testscommon.GasHandlerStub{}, FeeHandler: &mock.FeeAccumulatorStub{}, @@ -3269,8 +3333,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{}, @@ -3307,14 +3371,16 @@ func TestTransactionCoordinator_CheckGasProvidedByMiniBlockInReceiverShardShould tx2GasLimit := uint64(1) dataPool := initDataPool(txHash) + accounts := initAccountsMock() + txCoordinatorArgs := ArgTransactionCoordinator{ Hasher: &hashingMocks.HasherMock{}, Marshalizer: &mock.MarshalizerMock{}, ShardCoordinator: mock.NewMultiShardsCoordinatorMock(3), - Accounts: initAccountsMock(), + Accounts: accounts, MiniBlockPool: dataPool.MiniBlocks(), RequestHandler: &testscommon.RequestHandlerStub{}, - PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock()), + PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock(), accounts), InterProcessors: createInterimProcessorContainer(), GasHandler: &testscommon.GasHandlerStub{}, FeeHandler: &mock.FeeAccumulatorStub{}, @@ -3326,8 +3392,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{}, @@ -3369,14 +3435,16 @@ func TestTransactionCoordinator_CheckGasProvidedByMiniBlockInReceiverShardShould tx3GasLimit := uint64(300000001) dataPool := initDataPool(txHash) + accounts := initAccountsMock() + txCoordinatorArgs := ArgTransactionCoordinator{ Hasher: &hashingMocks.HasherMock{}, Marshalizer: &mock.MarshalizerMock{}, ShardCoordinator: mock.NewMultiShardsCoordinatorMock(3), - Accounts: initAccountsMock(), + Accounts: accounts, MiniBlockPool: dataPool.MiniBlocks(), RequestHandler: &testscommon.RequestHandlerStub{}, - PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock()), + PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock(), accounts), InterProcessors: createInterimProcessorContainer(), GasHandler: &testscommon.GasHandlerStub{}, FeeHandler: &mock.FeeAccumulatorStub{}, @@ -3436,14 +3504,16 @@ func TestTransactionCoordinator_CheckGasProvidedByMiniBlockInReceiverShardShould tx3GasLimit := uint64(300) dataPool := initDataPool(txHash) + accounts := initAccountsMock() + txCoordinatorArgs := ArgTransactionCoordinator{ Hasher: &hashingMocks.HasherMock{}, Marshalizer: &mock.MarshalizerMock{}, ShardCoordinator: mock.NewMultiShardsCoordinatorMock(3), - Accounts: initAccountsMock(), + Accounts: accounts, MiniBlockPool: dataPool.MiniBlocks(), RequestHandler: &testscommon.RequestHandlerStub{}, - PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock()), + PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock(), accounts), InterProcessors: createInterimProcessorContainer(), GasHandler: &testscommon.GasHandlerStub{}, FeeHandler: &mock.FeeAccumulatorStub{}, @@ -3500,14 +3570,16 @@ func TestTransactionCoordinator_VerifyFeesShouldErrMissingTransaction(t *testing t.Parallel() dataPool := initDataPool(txHash) + accounts := initAccountsMock() + txCoordinatorArgs := ArgTransactionCoordinator{ Hasher: &hashingMocks.HasherMock{}, Marshalizer: &mock.MarshalizerMock{}, ShardCoordinator: mock.NewMultiShardsCoordinatorMock(3), - Accounts: initAccountsMock(), + Accounts: accounts, MiniBlockPool: dataPool.MiniBlocks(), RequestHandler: &testscommon.RequestHandlerStub{}, - PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock()), + PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock(), accounts), InterProcessors: createInterimProcessorContainer(), GasHandler: &testscommon.GasHandlerStub{}, FeeHandler: &mock.FeeAccumulatorStub{}, @@ -3555,14 +3627,16 @@ func TestTransactionCoordinator_VerifyFeesShouldErrMaxAccumulatedFeesExceeded(t tx1GasLimit := uint64(100) dataPool := initDataPool(txHash) + accounts := initAccountsMock() + txCoordinatorArgs := ArgTransactionCoordinator{ Hasher: &hashingMocks.HasherMock{}, Marshalizer: &mock.MarshalizerMock{}, ShardCoordinator: mock.NewMultiShardsCoordinatorMock(3), - Accounts: initAccountsMock(), + Accounts: accounts, MiniBlockPool: dataPool.MiniBlocks(), RequestHandler: &testscommon.RequestHandlerStub{}, - PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock()), + PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock(), accounts), InterProcessors: createInterimProcessorContainer(), GasHandler: &testscommon.GasHandlerStub{}, FeeHandler: &mock.FeeAccumulatorStub{}, @@ -3624,14 +3698,16 @@ func TestTransactionCoordinator_VerifyFeesShouldErrMaxDeveloperFeesExceeded(t *t tx1GasLimit := uint64(100) dataPool := initDataPool(txHash) + accounts := initAccountsMock() + txCoordinatorArgs := ArgTransactionCoordinator{ Hasher: &hashingMocks.HasherMock{}, Marshalizer: &mock.MarshalizerMock{}, ShardCoordinator: mock.NewMultiShardsCoordinatorMock(3), - Accounts: initAccountsMock(), + Accounts: accounts, MiniBlockPool: dataPool.MiniBlocks(), RequestHandler: &testscommon.RequestHandlerStub{}, - PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock()), + PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock(), accounts), InterProcessors: createInterimProcessorContainer(), GasHandler: &testscommon.GasHandlerStub{}, FeeHandler: &mock.FeeAccumulatorStub{}, @@ -3694,14 +3770,16 @@ func TestTransactionCoordinator_VerifyFeesShouldErrMaxAccumulatedFeesExceededWhe enableEpochsHandlerStub := enableEpochsHandlerMock.NewEnableEpochsHandlerStub() dataPool := initDataPool(txHash) + accounts := initAccountsMock() + txCoordinatorArgs := ArgTransactionCoordinator{ Hasher: &hashingMocks.HasherMock{}, Marshalizer: &mock.MarshalizerMock{}, ShardCoordinator: mock.NewMultiShardsCoordinatorMock(3), - Accounts: initAccountsMock(), + Accounts: accounts, MiniBlockPool: dataPool.MiniBlocks(), RequestHandler: &testscommon.RequestHandlerStub{}, - PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock()), + PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock(), accounts), InterProcessors: createInterimProcessorContainer(), GasHandler: &testscommon.GasHandlerStub{}, FeeHandler: &mock.FeeAccumulatorStub{}, @@ -3778,14 +3856,16 @@ func TestTransactionCoordinator_VerifyFeesShouldErrMaxDeveloperFeesExceededWhenS enableEpochsHandlerStub := enableEpochsHandlerMock.NewEnableEpochsHandlerStub() dataPool := initDataPool(txHash) + accounts := initAccountsMock() + txCoordinatorArgs := ArgTransactionCoordinator{ Hasher: &hashingMocks.HasherMock{}, Marshalizer: &mock.MarshalizerMock{}, ShardCoordinator: mock.NewMultiShardsCoordinatorMock(3), - Accounts: initAccountsMock(), + Accounts: accounts, MiniBlockPool: dataPool.MiniBlocks(), RequestHandler: &testscommon.RequestHandlerStub{}, - PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock()), + PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock(), accounts), InterProcessors: createInterimProcessorContainer(), GasHandler: &testscommon.GasHandlerStub{}, FeeHandler: &mock.FeeAccumulatorStub{}, @@ -3862,14 +3942,16 @@ func TestTransactionCoordinator_VerifyFeesShouldWork(t *testing.T) { enableEpochsHandlerStub := enableEpochsHandlerMock.NewEnableEpochsHandlerStub() dataPool := initDataPool(txHash) + accounts := initAccountsMock() + txCoordinatorArgs := ArgTransactionCoordinator{ Hasher: &hashingMocks.HasherMock{}, Marshalizer: &mock.MarshalizerMock{}, ShardCoordinator: mock.NewMultiShardsCoordinatorMock(3), - Accounts: initAccountsMock(), + Accounts: accounts, MiniBlockPool: dataPool.MiniBlocks(), RequestHandler: &testscommon.RequestHandlerStub{}, - PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock()), + PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock(), accounts), InterProcessors: createInterimProcessorContainer(), GasHandler: &testscommon.GasHandlerStub{}, FeeHandler: &mock.FeeAccumulatorStub{}, @@ -3949,14 +4031,16 @@ func TestTransactionCoordinator_GetMaxAccumulatedAndDeveloperFeesShouldErr(t *te t.Parallel() dataPool := initDataPool(txHash) + accounts := initAccountsMock() + txCoordinatorArgs := ArgTransactionCoordinator{ Hasher: &hashingMocks.HasherMock{}, Marshalizer: &mock.MarshalizerMock{}, ShardCoordinator: mock.NewMultiShardsCoordinatorMock(3), - Accounts: initAccountsMock(), + Accounts: accounts, MiniBlockPool: dataPool.MiniBlocks(), RequestHandler: &testscommon.RequestHandlerStub{}, - PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock()), + PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock(), accounts), InterProcessors: createInterimProcessorContainer(), GasHandler: &testscommon.GasHandlerStub{}, FeeHandler: &mock.FeeAccumulatorStub{}, @@ -4001,14 +4085,16 @@ func TestTransactionCoordinator_GetMaxAccumulatedAndDeveloperFeesShouldWork(t *t tx3GasLimit := uint64(300) dataPool := initDataPool(txHash) + accounts := initAccountsMock() + txCoordinatorArgs := ArgTransactionCoordinator{ Hasher: &hashingMocks.HasherMock{}, Marshalizer: &mock.MarshalizerMock{}, ShardCoordinator: mock.NewMultiShardsCoordinatorMock(3), - Accounts: initAccountsMock(), + Accounts: accounts, MiniBlockPool: dataPool.MiniBlocks(), RequestHandler: &testscommon.RequestHandlerStub{}, - PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock()), + PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock(), accounts), InterProcessors: createInterimProcessorContainer(), GasHandler: &testscommon.GasHandlerStub{}, FeeHandler: &mock.FeeAccumulatorStub{}, @@ -4067,14 +4153,16 @@ func TestTransactionCoordinator_RevertIfNeededShouldWork(t *testing.T) { numTxsFeesReverted := 0 dataPool := initDataPool(txHash) + accounts := initAccountsMock() + txCoordinatorArgs := ArgTransactionCoordinator{ Hasher: &hashingMocks.HasherMock{}, Marshalizer: &mock.MarshalizerMock{}, ShardCoordinator: mock.NewMultiShardsCoordinatorMock(3), - Accounts: initAccountsMock(), + Accounts: accounts, MiniBlockPool: dataPool.MiniBlocks(), RequestHandler: &testscommon.RequestHandlerStub{}, - PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock()), + PreProcessors: createPreProcessorContainerWithDataPool(dataPool, FeeHandlerMock(), accounts), InterProcessors: createInterimProcessorContainer(), GasHandler: &mock.GasHandlerMock{ RestoreGasSinceLastResetCalled: func(key []byte) { diff --git a/process/coordinator/transactionType.go b/process/coordinator/transactionType.go index 32081a1ac0e..45097d89c50 100644 --- a/process/coordinator/transactionType.go +++ b/process/coordinator/transactionType.go @@ -77,59 +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 } + isRelayedV3 := common.IsRelayedTxV3(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 918b6069212..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,39 @@ 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) { + 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, 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) { @@ -488,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/dataValidators/txValidator.go b/process/dataValidators/txValidator.go index 9c72be1d89a..d043f207ac0 100644 --- a/process/dataValidators/txValidator.go +++ b/process/dataValidators/txValidator.go @@ -5,6 +5,8 @@ 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" @@ -74,14 +76,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 txValue.Sign() > 0 +} + func (txv *txValidator) checkAccount( interceptedTx process.InterceptedTransactionHandler, accountHandler vmcommon.AccountHandler, @@ -91,24 +106,41 @@ 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() + 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 { 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 +150,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, ) @@ -131,7 +162,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 551b18928d1..31fa230ec39 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,101 @@ 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 { + 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 + + 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"), + Value: big.NewInt(0), + } + } + 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/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/economics/economicsData.go b/process/economics/economicsData.go index 5b7ce045237..84e161ef86f 100644 --- a/process/economics/economicsData.go +++ b/process/economics/economicsData.go @@ -488,15 +488,19 @@ 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) + } + + gasLimit += ed.getExtraGasLimitRelayedTx(txInstance, epoch) } return gasLimit @@ -575,6 +579,15 @@ func (ed *economicsData) ComputeGasLimitBasedOnBalance(tx data.TransactionWithFe return ed.ComputeGasLimitBasedOnBalanceInEpoch(tx, balance, currentEpoch) } +// ComputeGasUnitsFromRefundValue will compute the gas unit based on the refund value +func (ed *economicsData) ComputeGasUnitsFromRefundValue(tx data.TransactionWithFeeHandler, refundValue *big.Int, epoch uint32) uint64 { + gasPrice := ed.GasPriceForProcessingInEpoch(tx, epoch) + refund := big.NewInt(0).Set(refundValue) + gasUnits := refund.Div(refund, big.NewInt(int64(gasPrice))) + + return gasUnits.Uint64() +} + // ComputeGasLimitBasedOnBalanceInEpoch will compute gas limit for the given transaction based on the balance in a specific epoch func (ed *economicsData) ComputeGasLimitBasedOnBalanceInEpoch(tx data.TransactionWithFeeHandler, balance *big.Int, epoch uint32) (uint64, error) { balanceWithoutTransferValue := big.NewInt(0).Sub(balance, tx.GetValue()) @@ -605,6 +618,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/errors.go b/process/errors.go index 1d4eaee569a..f03e12a276f 100644 --- a/process/errors.go +++ b/process/errors.go @@ -1062,9 +1062,6 @@ var ErrNilIsMaxBlockSizeReachedHandler = errors.New("nil handler for max block s // ErrNilTxMaxTotalCostHandler signals a nil transaction max total cost var ErrNilTxMaxTotalCostHandler = errors.New("nil transaction max total cost") -// ErrNilAccountTxsPerShard signals a nil mapping for account transactions to shard -var ErrNilAccountTxsPerShard = errors.New("nil account transactions per shard mapping") - // ErrScheduledRootHashDoesNotMatch signals that scheduled root hash does not match var ErrScheduledRootHashDoesNotMatch = errors.New("scheduled root hash does not match") @@ -1236,6 +1233,18 @@ 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") + +// 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") + // ErrNilShardBootstrap signals that a nil shard bootstrap was provided var ErrNilShardBootstrap = errors.New("nil shard bootstrap") 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 b76ecb8f847..dc00073b0e2 100644 --- a/process/interface.go +++ b/process/interface.go @@ -42,6 +42,7 @@ import ( type TransactionProcessor interface { ProcessTransaction(transaction *transaction.Transaction) (vmcommon.ReturnCode, error) VerifyTransaction(transaction *transaction.Transaction) error + VerifyGuardian(tx *transaction.Transaction, account state.UserAccountHandler) error GetSenderAndReceiverAccounts(transaction *transaction.Transaction) (state.UserAccountHandler, state.UserAccountHandler, error) IsInterfaceNil() bool } @@ -72,7 +73,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 } @@ -724,6 +725,7 @@ type feeHandler interface { ComputeGasUsedAndFeeBasedOnRefundValue(tx data.TransactionWithFeeHandler, refundValue *big.Int) (uint64, *big.Int) ComputeTxFeeBasedOnGasUsed(tx data.TransactionWithFeeHandler, gasUsed uint64) *big.Int ComputeGasLimitBasedOnBalance(tx data.TransactionWithFeeHandler, balance *big.Int) (uint64, error) + ComputeGasUnitsFromRefundValue(tx data.TransactionWithFeeHandler, refundValue *big.Int, epoch uint32) uint64 ComputeTxFeeInEpoch(tx data.TransactionWithFeeHandler, epoch uint32) *big.Int ComputeGasLimitInEpoch(tx data.TransactionWithFeeHandler, epoch uint32) uint64 ComputeGasUsedAndFeeBasedOnRefundValueInEpoch(tx data.TransactionWithFeeHandler, refundValue *big.Int, epoch uint32) (uint64, *big.Int) diff --git a/process/mock/multipleShardsCoordinatorMock.go b/process/mock/multipleShardsCoordinatorMock.go index 1cae0174836..9daea53bba9 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 } @@ -57,7 +58,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/smartContract/process.go b/process/smartContract/process.go index 16b6f640067..9dddfd2933e 100644 --- a/process/smartContract/process.go +++ b/process/smartContract/process.go @@ -975,7 +975,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") @@ -1493,7 +1493,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 @@ -2784,7 +2784,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, scrData.GetHash(), scrData.GetDestination()) diff --git a/process/smartContract/process_test.go b/process/smartContract/process_test.go index e1a1f001987..389c1d263b4 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 0eef6b73a4c..f5eb3ce40f2 100644 --- a/process/smartContract/processorV2/processV2.go +++ b/process/smartContract/processorV2/processV2.go @@ -820,15 +820,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 } @@ -946,7 +948,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") @@ -1423,7 +1425,7 @@ func (sc *scProcessor) getOriginalTxHashIfIntraShardRelayedSCR( tx data.TransactionHandler, txHash []byte, ) []byte { - relayedSCR, isRelayed := isRelayedTx(tx) + relayedSCR, isRelayed := isRelayedSCR(tx) if !isRelayed { return txHash } @@ -1530,7 +1532,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 @@ -1593,7 +1595,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 } @@ -1679,7 +1681,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 @@ -1692,6 +1694,20 @@ func isRelayedTx(tx data.TransactionHandler) (*smartContractResult.SmartContract 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.IsRelayedTxV3(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( @@ -1701,7 +1717,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 { @@ -1936,14 +1952,25 @@ 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 cost.Cmp(big.NewInt(0)) == 0 { - return nil + fee := sc.economicsFee.ComputeTxFee(tx) + if !check.IfNil(feePayer) { + err = feePayer.SubFromBalance(fee) + if err != nil { + return err + } + + err = sc.saveAccount(feePayer) + if err != nil { + return err + } } - err = acntSnd.SubFromBalance(cost) + err = acntSnd.SubFromBalance(tx.GetValue()) if err != nil { return err } @@ -1951,6 +1978,29 @@ 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.IsRelayedTxV3(tx) { + return acntSnd, nil + } + + relayedTx, ok := tx.(data.RelayedTransactionHandler) + if !ok { + return acntSnd, nil + } + + relayerIsSender := bytes.Equal(relayedTx.GetRelayerAddr(), tx.GetSndAddr()) + if relayerIsSender { + return acntSnd, nil // do not load the same account twice + } + + account, err := sc.scProcessorHelper.GetAccountFromAddress(relayedTx.GetRelayerAddr()) + if err != nil { + return nil, err + } + + return account, nil +} + func (sc *scProcessor) processVMOutput( vmInput *vmcommon.VMInput, vmOutput *vmcommon.VMOutput, @@ -2272,11 +2322,7 @@ func createBaseSCR( result.CallType = vmData.DirectCall setOriginalTxHash(result, txHash, tx) - relayedTx, isRelayed := isRelayedTx(tx) - if isRelayed { - result.RelayedValue = big.NewInt(0) - result.RelayerAddr = relayedTx.RelayerAddr - } + result.RelayerAddr, result.RelayedValue = getRelayedValues(tx) return result } @@ -2314,11 +2360,8 @@ func (sc *scProcessor) createAsyncCallBackSCRFromVMOutput( OriginalSender: origScr.GetOriginalSender(), } setOriginalTxHash(scr, txHash, tx) - relayedTx, isRelayed := isRelayedTx(tx) - if isRelayed { - scr.RelayedValue = big.NewInt(0) - scr.RelayerAddr = relayedTx.RelayerAddr - } + + scr.RelayerAddr, scr.RelayedValue = getRelayedValues(tx) sc.addVMOutputResultsToSCR(vmOutput, scr) @@ -2583,29 +2626,7 @@ func (sc *scProcessor) createSCRForSenderAndRelayer( rcvAddress = tx.GetRcvAddr() } - var refundGasToRelayerSCR *smartContractResult.SmartContractResult - relayedSCR, isRelayed := isRelayedTx(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 - } + refundGasToRelayerSCR := sc.createRefundGasToRelayerSCRIfNeeded(tx, txHash, callType, gasRefund) scTx := &smartContractResult.SmartContractResult{} scTx.Value = big.NewInt(0).Set(storageFreeRefund) @@ -2636,6 +2657,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, + txHash, + relayedSCR.OriginalTxHash, + gasRefund) + } + + isRelayedV3 := common.IsRelayedTxV3(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))...) @@ -2691,7 +2777,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, scrData.GetHash(), scrData.GetDestination()) diff --git a/process/smartContract/processorV2/process_test.go b/process/smartContract/processorV2/process_test.go index 67646711706..420bf02d1a2 100644 --- a/process/smartContract/processorV2/process_test.go +++ b/process/smartContract/processorV2/process_test.go @@ -3130,8 +3130,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) @@ -3191,8 +3191,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) @@ -3254,8 +3254,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() @@ -3328,8 +3328,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/sovereignSCProcess.go b/process/smartContract/processorV2/sovereignSCProcess.go index ce251e7a56c..c997c6979e0 100644 --- a/process/smartContract/processorV2/sovereignSCProcess.go +++ b/process/smartContract/processorV2/sovereignSCProcess.go @@ -7,8 +7,9 @@ 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/smartContractResult" - "github.com/multiversx/mx-chain-go/process" vmcommon "github.com/multiversx/mx-chain-vm-common-go" + + "github.com/multiversx/mx-chain-go/process" ) // SovereignSCProcessArgs - arguments for creating a new sovereign smart contract processor @@ -77,7 +78,7 @@ func (sc *sovereignSCProcessor) ProcessSmartContractResult(scr *smartContractRes return returnCode, err } - txType, _ := sc.txTypeHandler.ComputeTransactionType(scr) + txType, _, _ := sc.txTypeHandler.ComputeTransactionType(scr) switch txType { case process.BuiltInFunctionCall: err = sc.checkBuiltInFuncCall(string(scr.Data)) diff --git a/process/smartContract/processorV2/vmInputV2.go b/process/smartContract/processorV2/vmInputV2.go index 06c4c3f0ad2..81dd1f9360c 100644 --- a/process/smartContract/processorV2/vmInputV2.go +++ b/process/smartContract/processorV2/vmInputV2.go @@ -40,10 +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 := isRelayedTx(tx) - if isRelayed { - vmInput.RelayerAddr = relayedTx.RelayerAddr - } + vmInput.RelayerAddr, _ = getRelayedValues(tx) vmInput.GasProvided, err = sc.prepareGasProvided(tx) if err != nil { diff --git a/process/transaction/baseProcess.go b/process/transaction/baseProcess.go index 001e4ba3219..3885722465f 100644 --- a/process/transaction/baseProcess.go +++ b/process/transaction/baseProcess.go @@ -119,7 +119,17 @@ func (txProc *baseTxProcessor) checkTxValues( acntSnd, acntDst state.UserAccountHandler, isUserTxOfRelayed bool, ) error { - err := txProc.verifyGuardian(tx, acntSnd) + + if common.IsRelayedTxV3(tx) { + relayerAccount, err := txProc.getAccountFromAddress(tx.RelayerAddr) + if err != nil { + return err + } + + return txProc.checkUserTxOfRelayedV3Values(tx, acntSnd, acntDst, relayerAccount) + } + + err := txProc.VerifyGuardian(tx, acntSnd) if err != nil { return err } @@ -174,6 +184,86 @@ func (txProc *baseTxProcessor) checkTxValues( return nil } +func (txProc *baseTxProcessor) checkUserTxOfRelayedV3Values( + tx *transaction.Transaction, + senderAccount state.UserAccountHandler, + 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) + if err != nil { + return err + } + + if tx.GasLimit < txProc.economicsFee.ComputeGasLimit(tx) { + return process.ErrNotEnoughGas + } + + if check.IfNil(relayerAccount) { + return nil + } + + txFee := txProc.economicsFee.ComputeTxFee(tx) + + if relayerAccount.GetBalance().Cmp(txFee) < 0 { + return fmt.Errorf("%w, has: %s, wanted: %s", + process.ErrInsufficientFee, + relayerAccount.GetBalance().String(), + txFee.String(), + ) + } + + if senderAccount.GetBalance().Cmp(tx.Value) < 0 { + return process.ErrInsufficientFunds + } + + return nil +} + +func (txProc *baseTxProcessor) getFeePayer( + tx *transaction.Transaction, + senderAccount state.UserAccountHandler, + destinationAccount state.UserAccountHandler, +) (state.UserAccountHandler, bool, error) { + if !common.IsRelayedTxV3(tx) { + return senderAccount, false, nil + } + + relayerIsSender := bytes.Equal(tx.RelayerAddr, tx.SndAddr) + if relayerIsSender { + return senderAccount, true, nil // do not load the same account twice + } + + relayerIsDestination := bytes.Equal(tx.RelayerAddr, tx.RcvAddr) + if relayerIsDestination { + return destinationAccount, true, nil // do not load the same account twice + } + + acntRelayer, err := txProc.getAccountFromAddress(tx.RelayerAddr) + if err != nil { + return nil, true, err + } + + return acntRelayer, true, nil +} + func (txProc *baseTxProcessor) computeInnerTxFee(tx *transaction.Transaction) *big.Int { if txProc.enableEpochsHandler.IsFlagEnabled(common.FixRelayedBaseCostFlag) { return txProc.computeInnerTxFeeAfterBaseCostFix(tx) @@ -183,7 +273,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) } @@ -297,7 +387,8 @@ func (txProc *baseTxProcessor) checkGuardedAccountUnguardedTxPermission(tx *tran return nil } -func (txProc *baseTxProcessor) verifyGuardian(tx *transaction.Transaction, account state.UserAccountHandler) error { +// VerifyGuardian does the guardian verification +func (txProc *baseTxProcessor) VerifyGuardian(tx *transaction.Transaction, account state.UserAccountHandler) error { if check.IfNil(account) { return nil } diff --git a/process/transaction/baseProcess_test.go b/process/transaction/baseProcess_test.go index 7795c1a0f6a..7d4605239a9 100644 --- a/process/transaction/baseProcess_test.go +++ b/process/transaction/baseProcess_test.go @@ -231,7 +231,7 @@ func TestBaseTxProcessor_VerifyGuardian(t *testing.T) { t.Parallel() localBaseProc := baseProc - err := localBaseProc.verifyGuardian(&transaction.Transaction{}, nil) + err := localBaseProc.VerifyGuardian(&transaction.Transaction{}, nil) assert.Nil(t, err) }) t.Run("guarded account with a not guarded transaction should error", func(t *testing.T) { @@ -244,7 +244,7 @@ func TestBaseTxProcessor_VerifyGuardian(t *testing.T) { }, } - err := localBaseProc.verifyGuardian(&transaction.Transaction{}, guardedAccount) + err := localBaseProc.VerifyGuardian(&transaction.Transaction{}, guardedAccount) assert.ErrorIs(t, err, process.ErrTransactionNotExecutable) assert.Contains(t, err.Error(), "not allowed to bypass guardian") }) @@ -258,7 +258,7 @@ func TestBaseTxProcessor_VerifyGuardian(t *testing.T) { }, } - err := localBaseProc.verifyGuardian(&transaction.Transaction{}, notGuardedAccount) + err := localBaseProc.VerifyGuardian(&transaction.Transaction{}, notGuardedAccount) assert.ErrorIs(t, err, process.ErrTransactionNotExecutable) assert.Contains(t, err.Error(), process.ErrGuardedTransactionNotExpected.Error()) }) @@ -272,7 +272,7 @@ func TestBaseTxProcessor_VerifyGuardian(t *testing.T) { }, } - err := localBaseProc.verifyGuardian(&transaction.Transaction{}, notGuardedAccount) + err := localBaseProc.VerifyGuardian(&transaction.Transaction{}, notGuardedAccount) assert.Nil(t, err) }) t.Run("get active guardian fails should error", func(t *testing.T) { @@ -290,7 +290,7 @@ func TestBaseTxProcessor_VerifyGuardian(t *testing.T) { }, } - err := localBaseProc.verifyGuardian(&transaction.Transaction{}, guardedAccount) + err := localBaseProc.VerifyGuardian(&transaction.Transaction{}, guardedAccount) assert.ErrorIs(t, err, process.ErrTransactionNotExecutable) assert.Contains(t, err.Error(), expectedErr.Error()) }) @@ -309,7 +309,7 @@ func TestBaseTxProcessor_VerifyGuardian(t *testing.T) { }, } - err := localBaseProc.verifyGuardian(tx, guardedAccount) + err := localBaseProc.VerifyGuardian(tx, guardedAccount) assert.ErrorIs(t, err, process.ErrTransactionNotExecutable) assert.Contains(t, err.Error(), process.ErrTransactionAndAccountGuardianMismatch.Error()) }) @@ -328,7 +328,7 @@ func TestBaseTxProcessor_VerifyGuardian(t *testing.T) { }, } - err := localBaseProc.verifyGuardian(tx, guardedAccount) + err := localBaseProc.VerifyGuardian(tx, guardedAccount) assert.Nil(t, err) }) } diff --git a/process/transaction/export_test.go b/process/transaction/export_test.go index cd657c3991d..c6ff2791b45 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" @@ -55,6 +56,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 +64,7 @@ func (txProc *txProcessor) ProcessUserTx( userTx, relayedTxValue, relayedNonce, + relayerAddr, originalTxHash) } @@ -100,9 +103,9 @@ func (inTx *InterceptedTransaction) CheckMaxGasPrice() error { return inTx.checkMaxGasPrice() } -// VerifyGuardian calls the un-exported method verifyGuardian -func (txProc *txProcessor) VerifyGuardian(tx *transaction.Transaction, account state.UserAccountHandler) error { - return txProc.verifyGuardian(tx, account) +// SetEnableEpochsHandler sets the internal enable epochs handler +func (inTx *InterceptedTransaction) SetEnableEpochsHandler(handler common.EnableEpochsHandler) { + inTx.enableEpochsHandler = handler } // ShouldIncreaseNonce calls the un-exported method shouldIncreaseNonce diff --git a/process/transaction/interceptedTransaction.go b/process/transaction/interceptedTransaction.go index 75b9a65ae7c..53722cad96f 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" @@ -42,6 +43,7 @@ type InterceptedTransaction struct { sndShard uint32 isForCurrentShard bool enableSignedTxWithHash bool + enableEpochsHandler common.EnableEpochsHandler } // NewInterceptedTransaction returns a new instance of InterceptedTransaction @@ -61,6 +63,7 @@ func NewInterceptedTransaction( enableSignedTxWithHash bool, txSignHasher hashing.Hasher, txVersionChecker process.TxVersionCheckerHandler, + enableEpochsHandler common.EnableEpochsHandler, ) (*InterceptedTransaction, error) { if txBuff == nil { @@ -105,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 { @@ -127,6 +133,7 @@ func NewInterceptedTransaction( enableSignedTxWithHash: enableSignedTxWithHash, txVersionChecker: txVersionChecker, txSignHasher: txSignHasher, + enableEpochsHandler: enableEpochsHandler, } err = inTx.processFields(txBuff) @@ -189,6 +196,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 +217,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 common.IsValidRelayedTxV3(userTx) { + return process.ErrRecursiveRelayedTxIsNotAllowed + } + + funcName, _, err := inTx.argsParser.ParseCallData(string(userTx.Data)) if err != nil { return nil } @@ -223,6 +239,47 @@ func isRelayedTx(funcName string) bool { core.RelayedTransactionV2 == funcName } +func (inTx *InterceptedTransaction) verifyIfRelayedTxV3(tx *transaction.Transaction) error { + if !common.IsRelayedTxV3(tx) { + return nil + } + + if !inTx.enableEpochsHandler.IsFlagEnabled(common.RelayedTransactionsV3Flag) { + return process.ErrRelayedTxV3Disabled + } + + if !common.IsValidRelayedTxV3(tx) { + return process.ErrInvalidRelayedTxV3 + } + + err := inTx.integrity(tx) + if err != nil { + 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 + 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 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 +329,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 +427,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..7459e586241 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" @@ -36,6 +38,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") @@ -114,6 +117,7 @@ func createInterceptedTxWithTxFeeHandlerAndVersionChecker(tx *dataTransaction.Tr false, &hashingMocks.HasherMock{}, txVerChecker, + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) } @@ -157,6 +161,7 @@ func createInterceptedTxFromPlainTx(tx *dataTransaction.Transaction, txFeeHandle false, &hashingMocks.HasherMock{}, versioning.NewTxVersionChecker(minTxVersion), + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) } @@ -170,7 +175,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) { @@ -179,6 +185,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, @@ -200,6 +210,7 @@ func createInterceptedTxFromPlainTxWithArgParser(tx *dataTransaction.Transaction false, &hashingMocks.HasherMock{}, versioning.NewTxVersionChecker(tx.Version), + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(common.RelayedTransactionsV3Flag), ) } @@ -224,6 +235,7 @@ func TestNewInterceptedTransaction_NilBufferShouldErr(t *testing.T) { false, &hashingMocks.HasherMock{}, versioning.NewTxVersionChecker(1), + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) assert.Nil(t, txi) @@ -249,6 +261,7 @@ func TestNewInterceptedTransaction_NilArgsParser(t *testing.T) { false, &hashingMocks.HasherMock{}, versioning.NewTxVersionChecker(1), + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) assert.Nil(t, txi) @@ -274,6 +287,7 @@ func TestNewInterceptedTransaction_NilVersionChecker(t *testing.T) { false, &hashingMocks.HasherMock{}, nil, + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) assert.Nil(t, txi) @@ -299,6 +313,7 @@ func TestNewInterceptedTransaction_NilMarshalizerShouldErr(t *testing.T) { false, &hashingMocks.HasherMock{}, versioning.NewTxVersionChecker(1), + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) assert.Nil(t, txi) @@ -324,6 +339,7 @@ func TestNewInterceptedTransaction_NilSignMarshalizerShouldErr(t *testing.T) { false, &hashingMocks.HasherMock{}, versioning.NewTxVersionChecker(1), + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) assert.Nil(t, txi) @@ -349,6 +365,7 @@ func TestNewInterceptedTransaction_NilHasherShouldErr(t *testing.T) { false, &hashingMocks.HasherMock{}, versioning.NewTxVersionChecker(1), + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) assert.Nil(t, txi) @@ -374,6 +391,7 @@ func TestNewInterceptedTransaction_NilKeyGenShouldErr(t *testing.T) { false, &hashingMocks.HasherMock{}, versioning.NewTxVersionChecker(1), + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) assert.Nil(t, txi) @@ -399,6 +417,7 @@ func TestNewInterceptedTransaction_NilSignerShouldErr(t *testing.T) { false, &hashingMocks.HasherMock{}, versioning.NewTxVersionChecker(1), + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) assert.Nil(t, txi) @@ -424,6 +443,7 @@ func TestNewInterceptedTransaction_NilPubkeyConverterShouldErr(t *testing.T) { false, &hashingMocks.HasherMock{}, versioning.NewTxVersionChecker(1), + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) assert.Nil(t, txi) @@ -449,6 +469,7 @@ func TestNewInterceptedTransaction_NilCoordinatorShouldErr(t *testing.T) { false, &hashingMocks.HasherMock{}, versioning.NewTxVersionChecker(1), + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) assert.Nil(t, txi) @@ -474,6 +495,7 @@ func TestNewInterceptedTransaction_NilFeeHandlerShouldErr(t *testing.T) { false, &hashingMocks.HasherMock{}, versioning.NewTxVersionChecker(1), + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) assert.Nil(t, txi) @@ -499,6 +521,7 @@ func TestNewInterceptedTransaction_NilWhiteListerVerifiedTxsShouldErr(t *testing false, &hashingMocks.HasherMock{}, versioning.NewTxVersionChecker(1), + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) assert.Nil(t, txi) @@ -524,6 +547,7 @@ func TestNewInterceptedTransaction_InvalidChainIDShouldErr(t *testing.T) { false, &hashingMocks.HasherMock{}, versioning.NewTxVersionChecker(1), + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) assert.Nil(t, txi) @@ -549,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() @@ -580,6 +631,7 @@ func TestNewInterceptedTransaction_UnmarshalingTxFailsShouldErr(t *testing.T) { false, &hashingMocks.HasherMock{}, versioning.NewTxVersionChecker(1), + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) assert.Nil(t, txi) @@ -1050,6 +1102,7 @@ func TestInterceptedTransaction_CheckValiditySignedWithHashButNotEnabled(t *test false, &hashingMocks.HasherMock{}, versioning.NewTxVersionChecker(minTxVersion), + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) err := txi.CheckValidity() @@ -1110,6 +1163,7 @@ func TestInterceptedTransaction_CheckValiditySignedWithHashShouldWork(t *testing true, &hashingMocks.HasherMock{}, versioning.NewTxVersionChecker(minTxVersion), + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) err := txi.CheckValidity() @@ -1195,6 +1249,7 @@ func TestInterceptedTransaction_ScTxDeployRecvShardIdShouldBeSendersShardId(t *t false, &hashingMocks.HasherMock{}, versioning.NewTxVersionChecker(minTxVersion), + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) assert.Nil(t, err) @@ -1334,6 +1389,7 @@ func TestInterceptedTransaction_CheckValiditySecondTimeDoesNotVerifySig(t *testi false, &hashingMocks.HasherMock{}, versioning.NewTxVersionChecker(minTxVersion), + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) require.Nil(t, err) @@ -1500,6 +1556,82 @@ 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 + + // flag not active should error + txi, _ = createInterceptedTxFromPlainTxWithArgParser(tx) + txi.SetEnableEpochsHandler(enableEpochsHandlerMock.NewEnableEpochsHandlerStub()) + 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() + 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() + assert.Nil(t, err) +} + // ------- IsInterfaceNil func TestInterceptedTransaction_IsInterfaceNil(t *testing.T) { t.Parallel() @@ -1629,6 +1761,7 @@ func TestInterceptedTransaction_Fee(t *testing.T) { false, &hashingMocks.HasherMock{}, versioning.NewTxVersionChecker(0), + enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), ) assert.Equal(t, big.NewInt(0), txin.Fee()) @@ -1672,6 +1805,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 13d6fd4715b..090d4ad89e4 100644 --- a/process/transaction/metaProcess.go +++ b/process/transaction/metaProcess.go @@ -135,7 +135,7 @@ func (txProc *metaTxProcessor) ProcessTransaction(tx *transaction.Transaction) ( return 0, err } - txType, _ := txProc.txTypeHandler.ComputeTransactionType(tx) + 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 51910b537e2..a9b4d4d68b8 100644 --- a/process/transaction/shardProcess.go +++ b/process/transaction/shardProcess.go @@ -196,18 +196,18 @@ 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) { - receiptErr := txProc.executingFailedTransaction(tx, acntSnd, err) + receiptErr := txProc.executingFailedTransaction(tx, acntSnd, acntDst, err) if receiptErr != nil { return 0, receiptErr } } if errors.Is(err, process.ErrUserNameDoesNotMatch) && txProc.enableEpochsHandler.IsFlagEnabled(common.RelayedTransactionsFlag) { - receiptErr := txProc.executingFailedTransaction(tx, acntSnd, err) + receiptErr := txProc.executingFailedTransaction(tx, acntSnd, acntDst, err) if receiptErr != nil { return vmcommon.UserError, receiptErr } @@ -223,6 +223,13 @@ 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) @@ -242,7 +249,7 @@ func (txProc *txProcessor) ProcessTransaction(tx *transaction.Transaction) (vmco return txProc.processRelayedTxV2(tx, acntSnd, acntDst) } - return vmcommon.UserError, txProc.executingFailedTransaction(tx, acntSnd, process.ErrWrongTransaction) + return vmcommon.UserError, txProc.executingFailedTransaction(tx, acntSnd, acntDst, process.ErrWrongTransaction) } func (txProc *txProcessor) executeAfterFailedMoveBalanceTransaction( @@ -289,14 +296,20 @@ func (txProc *txProcessor) executeAfterFailedMoveBalanceTransaction( func (txProc *txProcessor) executingFailedTransaction( tx *transaction.Transaction, acntSnd state.UserAccountHandler, + acntDst state.UserAccountHandler, txError error, ) error { if check.IfNil(acntSnd) { return nil } + feePayer, isRelayedV3, err := txProc.getFeePayer(tx, acntSnd, acntDst) + if err != nil { + return err + } + txFee := txProc.economicsFee.ComputeTxFee(tx) - err := acntSnd.SubFromBalance(txFee) + err = feePayer.SubFromBalance(txFee) if err != nil { return err } @@ -328,24 +341,32 @@ 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 } 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) @@ -362,33 +383,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(feePayer) if err != nil { return nil, nil, err } @@ -417,26 +438,28 @@ func (txProc *txProcessor) processTxFee( if dstShardTxType != process.MoveBalance || (!txProc.enableEpochsHandler.IsFlagEnabled(common.MetaProtectionFlag) && isCrossShardSCCall) { - err := acntSnd.SubFromBalance(totalCost) + 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(feePayer) + if err != nil { + return nil, nil, err + } + return moveBalanceFee, totalCost, nil } -func (txProc *txProcessor) checkIfValidTxToMetaChain( - tx *transaction.Transaction, - adrDst []byte, -) error { +func (txProc *txProcessor) checkIfValidTxToMetaChain(tx *transaction.Transaction) error { - destShardId := txProc.shardCoordinator.ComputeId(adrDst) + destShardId := txProc.shardCoordinator.ComputeId(tx.RcvAddr) if destShardId != core.MetachainShardId { return nil } @@ -464,7 +487,11 @@ func (txProc *txProcessor) processMoveBalance( isUserTxOfRelayed bool, ) error { - moveBalanceCost, totalCost, err := txProc.processTxFee(tx, acntSrc, acntDst, destShardTxType, isUserTxOfRelayed) + feePayer, _, err := txProc.getFeePayer(tx, acntSrc, acntDst) + if err != nil { + return nil + } + moveBalanceCost, totalCost, err := txProc.processTxFee(tx, feePayer, acntDst, destShardTxType, isUserTxOfRelayed) if err != nil { return err } @@ -500,7 +527,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 { @@ -528,7 +555,7 @@ func (txProc *txProcessor) processMoveBalance( 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 } @@ -604,7 +631,12 @@ func (txProc *txProcessor) finishExecutionOfRelayedTx( userTx *transaction.Transaction, ) (vmcommon.ReturnCode, error) { computedFees := txProc.computeRelayedTxFees(tx, userTx) - txHash, err := txProc.processTxAtRelayer(relayerAcnt, computedFees.totalFee, computedFees.relayerFee, tx) + txHash, err := txProc.processTxAtRelayer( + relayerAcnt, + computedFees.totalFee, + computedFees.relayerFee, + tx, + tx.Value) if err != nil { return 0, err } @@ -618,7 +650,7 @@ func (txProc *txProcessor) finishExecutionOfRelayedTx( return 0, err } - return txProc.processUserTx(tx, userTx, tx.Value, tx.Nonce, txHash) + return txProc.processUserTx(tx, userTx, tx.Value, tx.Nonce, tx.SndAddr, txHash) } func (txProc *txProcessor) processTxAtRelayer( @@ -626,6 +658,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 +666,7 @@ func (txProc *txProcessor) processTxAtRelayer( } if !check.IfNil(relayerAcnt) { - err = relayerAcnt.SubFromBalance(tx.GetValue()) + err = relayerAcnt.SubFromBalance(valueToSubFromRelayer) if err != nil { return nil, err } @@ -656,6 +689,10 @@ func (txProc *txProcessor) processTxAtRelayer( } func (txProc *txProcessor) addFeeAndValueToDest(acntDst state.UserAccountHandler, txValue *big.Int, remainingFee *big.Int) error { + if check.IfNil(acntDst) { + return nil + } + err := acntDst.AddToBalance(txValue) if err != nil { return err @@ -669,23 +706,48 @@ func (txProc *txProcessor) addFeeAndValueToDest(acntDst state.UserAccountHandler return txProc.accounts.SaveAccount(acntDst) } +func (txProc *txProcessor) verifyRelayedTxV3(tx *transaction.Transaction) error { + if !txProc.enableEpochsHandler.IsFlagEnabled(common.RelayedTransactionsV3Flag) { + return fmt.Errorf("%w, %s", process.ErrTransactionNotExecutable, process.ErrRelayedTxV3Disabled) + } + + if !txProc.shardCoordinator.SameShard(tx.RelayerAddr, tx.SndAddr) { + return fmt.Errorf("%w, %s", process.ErrTransactionNotExecutable, process.ErrShardIdMissmatch) + } + + if bytes.Equal(tx.RelayerAddr, tx.GuardianAddr) { + return fmt.Errorf("%w, %s", process.ErrTransactionNotExecutable, process.ErrRelayedByGuardianNotAllowed) + } + + relayerAccount, err := txProc.getAccountFromAddress(tx.RelayerAddr) + if err != nil { + return fmt.Errorf("%w, %s", process.ErrTransactionNotExecutable, err) + } + + if !check.IfNil(relayerAccount) && relayerAccount.IsGuarded() { + return fmt.Errorf("%w, %s", process.ErrTransactionNotExecutable, process.ErrGuardedRelayerNotAllowed) + } + + return nil +} + func (txProc *txProcessor) processRelayedTxV2( tx *transaction.Transaction, relayerAcnt, acntDst state.UserAccountHandler, ) (vmcommon.ReturnCode, error) { if !txProc.enableEpochsHandler.IsFlagEnabled(common.RelayedTransactionsV2Flag) { - return vmcommon.UserError, txProc.executingFailedTransaction(tx, relayerAcnt, process.ErrRelayedTxV2Disabled) + return vmcommon.UserError, txProc.executingFailedTransaction(tx, relayerAcnt, acntDst, process.ErrRelayedTxV2Disabled) } if tx.GetValue().Cmp(big.NewInt(0)) != 0 { - return vmcommon.UserError, txProc.executingFailedTransaction(tx, relayerAcnt, process.ErrRelayedTxV2ZeroVal) + return vmcommon.UserError, txProc.executingFailedTransaction(tx, relayerAcnt, acntDst, process.ErrRelayedTxV2ZeroVal) } _, args, err := txProc.argsParser.ParseCallData(string(tx.GetData())) if err != nil { - return vmcommon.UserError, txProc.executingFailedTransaction(tx, relayerAcnt, err) + return vmcommon.UserError, txProc.executingFailedTransaction(tx, relayerAcnt, acntDst, err) } if len(args) != 4 { - return vmcommon.UserError, txProc.executingFailedTransaction(tx, relayerAcnt, process.ErrInvalidArguments) + return vmcommon.UserError, txProc.executingFailedTransaction(tx, relayerAcnt, acntDst, process.ErrInvalidArguments) } userTx := makeUserTxFromRelayedTxV2Args(args) @@ -706,31 +768,31 @@ func (txProc *txProcessor) processRelayedTx( return 0, err } if len(args) != 1 { - return vmcommon.UserError, txProc.executingFailedTransaction(tx, relayerAcnt, process.ErrInvalidArguments) + return vmcommon.UserError, txProc.executingFailedTransaction(tx, relayerAcnt, acntDst, process.ErrInvalidArguments) } if !txProc.enableEpochsHandler.IsFlagEnabled(common.RelayedTransactionsFlag) { - return vmcommon.UserError, txProc.executingFailedTransaction(tx, relayerAcnt, process.ErrRelayedTxDisabled) + return vmcommon.UserError, txProc.executingFailedTransaction(tx, relayerAcnt, acntDst, process.ErrRelayedTxDisabled) } userTx := &transaction.Transaction{} err = txProc.signMarshalizer.Unmarshal(userTx, args[0]) if err != nil { - return vmcommon.UserError, txProc.executingFailedTransaction(tx, relayerAcnt, err) + return vmcommon.UserError, txProc.executingFailedTransaction(tx, relayerAcnt, acntDst, err) } if !bytes.Equal(userTx.SndAddr, tx.RcvAddr) { - return vmcommon.UserError, txProc.executingFailedTransaction(tx, relayerAcnt, process.ErrRelayedTxBeneficiaryDoesNotMatchReceiver) + return vmcommon.UserError, txProc.executingFailedTransaction(tx, relayerAcnt, acntDst, process.ErrRelayedTxBeneficiaryDoesNotMatchReceiver) } if userTx.Value.Cmp(tx.Value) < 0 { - return vmcommon.UserError, txProc.executingFailedTransaction(tx, relayerAcnt, process.ErrRelayedTxValueHigherThenUserTxValue) + return vmcommon.UserError, txProc.executingFailedTransaction(tx, relayerAcnt, acntDst, process.ErrRelayedTxValueHigherThenUserTxValue) } if userTx.GasPrice != tx.GasPrice { - return vmcommon.UserError, txProc.executingFailedTransaction(tx, relayerAcnt, process.ErrRelayedGasPriceMissmatch) + return vmcommon.UserError, txProc.executingFailedTransaction(tx, relayerAcnt, acntDst, process.ErrRelayedGasPriceMissmatch) } remainingGasLimit := tx.GasLimit - txProc.economicsFee.ComputeGasLimit(tx) if userTx.GasLimit != remainingGasLimit { - return vmcommon.UserError, txProc.executingFailedTransaction(tx, relayerAcnt, process.ErrRelayedTxGasLimitMissmatch) + return vmcommon.UserError, txProc.executingFailedTransaction(tx, relayerAcnt, acntDst, process.ErrRelayedTxGasLimitMissmatch) } return txProc.finishExecutionOfRelayedTx(relayerAcnt, acntDst, tx, userTx) @@ -838,10 +900,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 +912,7 @@ func (txProc *txProcessor) processUserTx( } return vmcommon.UserError, txProc.executeFailedRelayedUserTx( userTx, - relayerAdr, + relayerAddr, relayedTxValue, relayedNonce, originalTx, @@ -858,7 +920,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) @@ -867,7 +929,7 @@ func (txProc *txProcessor) processUserTx( } return vmcommon.UserError, txProc.executeFailedRelayedUserTx( userTx, - relayerAdr, + relayerAddr, relayedTxValue, relayedNonce, originalTx, @@ -875,7 +937,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 +975,7 @@ func (txProc *txProcessor) processUserTx( } return vmcommon.UserError, txProc.executeFailedRelayedUserTx( userTx, - relayerAdr, + relayerAddr, relayedTxValue, relayedNonce, originalTx, @@ -924,7 +986,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..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" @@ -923,7 +924,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 +990,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 +1026,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 +1073,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) { @@ -1111,8 +1112,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 +1160,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 +1208,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 +1254,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) @@ -1324,7 +1325,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) { @@ -1573,8 +1574,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 +1622,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 +2182,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 +2245,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 +2308,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 +2371,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 +2434,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 +2497,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 +2560,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) @@ -2653,6 +2654,167 @@ 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 + txCopy.Nonce = acntSrc.GetNonce() + returnCode, err := txProc.ProcessTransaction(&txCopy) + assert.NoError(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 + txCopy.Nonce = acntSrc.GetNonce() + returnCode, err := txProcLocal.ProcessTransaction(&txCopy) + 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) { + argsCopy := args + argsCopy.ShardCoordinator = &mock.ShardCoordinatorStub{ + SameShardCalled: func(firstAddress, secondAddress []byte) bool { + return false + }, + } + txProcLocal, _ := txproc.NewTxProcessor(argsCopy) + require.NotNil(t, txProcLocal) + + txCopy := *tx + txCopy.Nonce = acntSrc.GetNonce() + returnCode, err := txProcLocal.ProcessTransaction(&txCopy) + 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) { + 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 + }, + Balance: big.NewInt(1), + }, nil + } + + return nil, errors.New("failure") + }, + } + txProcLocal, _ := txproc.NewTxProcessor(argsCopy) + require.NotNil(t, txProcLocal) + + txCopy := *tx + txCopy.Nonce = acntSrc.GetNonce() + returnCode, err := txProcLocal.ProcessTransaction(&txCopy) + 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) { + txCopy := *tx + txCopy.Nonce = acntSrc.GetNonce() + txCopy.GuardianAddr = txCopy.RelayerAddr + returnCode, err := txProc.ProcessTransaction(&txCopy) + 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) { + txCopy := *tx + txCopy.Nonce = acntSrc.GetNonce() + 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.ErrNotEnoughGas, err) + assert.Equal(t, vmcommon.UserError, returnCode) + }) +} + func TestTxProcessor_ConsumeMoveBalanceWithUserTx(t *testing.T) { t.Parallel() @@ -2755,13 +2917,13 @@ 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) - 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) } @@ -2818,13 +2980,13 @@ 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) - 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) } @@ -2881,13 +3043,13 @@ 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) - 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) } @@ -2944,13 +3106,13 @@ 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) - 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) } @@ -3007,13 +3169,13 @@ 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) - 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) } @@ -3074,13 +3236,13 @@ 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) - 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) } @@ -3143,13 +3305,13 @@ 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) - 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) } @@ -3365,13 +3527,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/state/accountsDB.go b/state/accountsDB.go index 4274e5138d6..707361ae68d 100644 --- a/state/accountsDB.go +++ b/state/accountsDB.go @@ -482,9 +482,7 @@ func (adb *AccountsDB) saveDataTrie(accountHandler baseAccountHandler) error { } func (adb *AccountsDB) saveAccountToTrie(accountHandler vmcommon.AccountHandler, mainTrie common.Trie) error { - log.Trace("accountsDB.saveAccountToTrie", - "address", hex.EncodeToString(accountHandler.AddressBytes()), - ) + log.Trace("accountsDB.saveAccountToTrie", "address", accountHandler.AddressBytes()) // pass the reference to marshaller, otherwise it will fail marshalling balance buff, err := adb.marshaller.Marshal(accountHandler) @@ -601,9 +599,7 @@ func (adb *AccountsDB) LoadAccount(address []byte) (vmcommon.AccountHandler, err return nil, fmt.Errorf("%w in LoadAccount", ErrNilAddress) } - log.Trace("accountsDB.LoadAccount", - "address", hex.EncodeToString(address), - ) + log.Trace("accountsDB.LoadAccount", "address", address) mainTrie := adb.getMainTrie() acnt, err := adb.getAccount(address, mainTrie) @@ -653,9 +649,7 @@ func (adb *AccountsDB) GetExistingAccount(address []byte) (vmcommon.AccountHandl return nil, fmt.Errorf("%w in GetExistingAccount", ErrNilAddress) } - log.Trace("accountsDB.GetExistingAccount", - "address", hex.EncodeToString(address), - ) + log.Trace("accountsDB.GetExistingAccount", "address", address) mainTrie := adb.getMainTrie() acnt, err := adb.getAccount(address, mainTrie) diff --git a/statusHandler/statusMetricsProvider.go b/statusHandler/statusMetricsProvider.go index 8050d335431..bc92e511c10 100644 --- a/statusHandler/statusMetricsProvider.go +++ b/statusHandler/statusMetricsProvider.go @@ -246,6 +246,7 @@ func (sm *statusMetrics) ConfigMetrics() (map[string]interface{}, error) { configMetrics[common.MetricMinGasPrice] = sm.uint64Metrics[common.MetricMinGasPrice] configMetrics[common.MetricMinGasLimit] = sm.uint64Metrics[common.MetricMinGasLimit] configMetrics[common.MetricExtraGasLimitGuardedTx] = sm.uint64Metrics[common.MetricExtraGasLimitGuardedTx] + configMetrics[common.MetricExtraGasLimitRelayedTx] = sm.uint64Metrics[common.MetricExtraGasLimitRelayedTx] configMetrics[common.MetricMaxGasPerTransaction] = sm.uint64Metrics[common.MetricMaxGasPerTransaction] configMetrics[common.MetricRoundDuration] = sm.uint64Metrics[common.MetricRoundDuration] configMetrics[common.MetricStartTime] = sm.uint64Metrics[common.MetricStartTime] @@ -379,6 +380,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..40303970ce5 100644 --- a/statusHandler/statusMetricsProvider_test.go +++ b/statusHandler/statusMetricsProvider_test.go @@ -179,6 +179,7 @@ func TestStatusMetrics_NetworkConfig(t *testing.T) { sm.SetUInt64Value(common.MetricMinGasPrice, 1000) sm.SetUInt64Value(common.MetricMinGasLimit, 50000) sm.SetUInt64Value(common.MetricExtraGasLimitGuardedTx, 50000) + sm.SetUInt64Value(common.MetricExtraGasLimitRelayedTx, 50000) sm.SetStringValue(common.MetricRewardsTopUpGradientPoint, "12345") sm.SetUInt64Value(common.MetricGasPerDataByte, 1500) sm.SetStringValue(common.MetricChainId, "local-id") @@ -202,6 +203,7 @@ func TestStatusMetrics_NetworkConfig(t *testing.T) { "erd_meta_consensus_group_size": uint64(25), "erd_min_gas_limit": uint64(50000), "erd_extra_gas_limit_guarded_tx": uint64(50000), + "erd_extra_gas_limit_relayed_tx": uint64(50000), "erd_min_gas_price": uint64(1000), "erd_min_transaction_version": uint64(2), "erd_num_metachain_nodes": uint64(50), @@ -402,6 +404,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 +536,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{}{ { diff --git a/storage/constants.go b/storage/constants.go index 9cd37571521..4924cbf06dd 100644 --- a/storage/constants.go +++ b/storage/constants.go @@ -19,9 +19,6 @@ const PathEpochPlaceholder = "[E]" // PathIdentifierPlaceholder represents the placeholder for the identifier in paths const PathIdentifierPlaceholder = "[I]" -// TxPoolNumTxsToPreemptivelyEvict instructs tx pool eviction algorithm to remove this many transactions when eviction takes place -const TxPoolNumTxsToPreemptivelyEvict = uint32(1000) - // DefaultDBPath is the default path for nodes databases const DefaultDBPath = "db" @@ -33,3 +30,12 @@ const DefaultStaticDbString = "Static" // DefaultShardString is the default folder root name for per shard databases const DefaultShardString = "Shard" + +// TxPoolSourceMeNumItemsToPreemptivelyEvict is a configuration of the eviction algorithm +const TxPoolSourceMeNumItemsToPreemptivelyEvict = uint32(50000) + +// TxPoolDestinationMeNumItemsToPreemptivelyEvict is a configuration of the eviction algorithm +const TxPoolDestinationMeNumItemsToPreemptivelyEvict = uint32(1000) + +// ShardedDataNumItemsToPreemptivelyEvict is a configuration of the eviction algorithm +const ShardedDataNumItemsToPreemptivelyEvict = uint32(1000) 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 diff --git a/storage/pruning/fullHistoryPruningStorer_test.go b/storage/pruning/fullHistoryPruningStorer_test.go index 0e0d43877e8..d1274499bb9 100644 --- a/storage/pruning/fullHistoryPruningStorer_test.go +++ b/storage/pruning/fullHistoryPruningStorer_test.go @@ -18,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" @@ -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) +} diff --git a/storage/pruning/pruningStorer.go b/storage/pruning/pruningStorer.go index b22d60f7557..864cfba9a6c 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) } shardID := core.GetShardIDString(ps.shardCoordinator.SelfId()) @@ -802,6 +802,11 @@ func (ps *PruningStorer) changeEpoch(header data.HeaderHandler) error { ps.activePersisters = append(singleItemPersisters, ps.activePersisters...) ps.persistersMapByEpoch[epoch] = newPersister + return ps.removeOldPersistersIfNeeded(header) +} + +func (ps *PruningStorer) removeOldPersistersIfNeeded(header data.HeaderHandler) error { + epoch := header.GetEpoch() wasExtended := ps.extendSavedEpochsIfNeeded(header) if wasExtended { if len(ps.activePersisters) > int(ps.numOfActivePersisters) { @@ -814,11 +819,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 } diff --git a/storage/txcache/txcache.go b/storage/txcache/txcache.go index 218a3749805..01e9212ea15 100644 --- a/storage/txcache/txcache.go +++ b/storage/txcache/txcache.go @@ -2,13 +2,20 @@ package txcache import ( "github.com/multiversx/mx-chain-storage-go/txcache" + "github.com/multiversx/mx-chain-storage-go/types" ) // WrappedTransaction contains a transaction, its hash and extra information type WrappedTransaction = txcache.WrappedTransaction -// TxGasHandler handles a transaction gas and gas cost -type TxGasHandler = txcache.TxGasHandler +// AccountState represents the state of an account (as seen by the mempool) +type AccountState = types.AccountState + +// MempoolHost provides blockchain information for mempool operations +type MempoolHost = txcache.MempoolHost + +// SelectionSession provides blockchain information for transaction selection +type SelectionSession = txcache.SelectionSession // ForEachTransaction is an iterator callback type ForEachTransaction = txcache.ForEachTransaction @@ -29,8 +36,8 @@ type DisabledCache = txcache.DisabledCache type CrossTxCache = txcache.CrossTxCache // NewTxCache creates a new transaction cache -func NewTxCache(config ConfigSourceMe, txGasHandler TxGasHandler) (*TxCache, error) { - return txcache.NewTxCache(config, txGasHandler) +func NewTxCache(config ConfigSourceMe, host MempoolHost) (*TxCache, error) { + return txcache.NewTxCache(config, host) } // NewDisabledCache creates a new disabled cache diff --git a/storage/txcache/txcache_test.go b/storage/txcache/txcache_test.go index cd0ded4f133..5b84daba2d5 100644 --- a/storage/txcache/txcache_test.go +++ b/storage/txcache/txcache_test.go @@ -16,37 +16,33 @@ func TestNewTxCache(t *testing.T) { t.Parallel() cfg := ConfigSourceMe{ - Name: "test", - NumChunks: 1, - NumBytesThreshold: 1000, - NumBytesPerSenderThreshold: 100, - CountThreshold: 10, - CountPerSenderThreshold: 100, - NumSendersToPreemptivelyEvict: 1, + Name: "test", + NumChunks: 1, + NumBytesThreshold: 1000, + NumBytesPerSenderThreshold: 100, + CountThreshold: 10, + CountPerSenderThreshold: 100, + NumItemsToPreemptivelyEvict: 1, } cache, err := NewTxCache(cfg, nil) assert.Nil(t, cache) - assert.Equal(t, common.ErrNilTxGasHandler, err) + assert.ErrorContains(t, err, "nil mempool host") }) t.Run("should work", func(t *testing.T) { t.Parallel() cfg := ConfigSourceMe{ - Name: "test", - NumChunks: 1, - NumBytesThreshold: 1000, - NumBytesPerSenderThreshold: 100, - CountThreshold: 10, - CountPerSenderThreshold: 100, - NumSendersToPreemptivelyEvict: 1, + Name: "test", + NumChunks: 1, + NumBytesThreshold: 1000, + NumBytesPerSenderThreshold: 100, + CountThreshold: 10, + CountPerSenderThreshold: 100, + NumItemsToPreemptivelyEvict: 1, } - cache, err := NewTxCache(cfg, &txcachemocks.TxGasHandlerMock{ - GasProcessingDivisor: 1, - MinimumGasPrice: 1, - MinimumGasMove: 1, - }) + cache, err := NewTxCache(cfg, txcachemocks.NewMempoolHostMock()) assert.NotNil(t, cache) assert.Nil(t, err) }) diff --git a/testscommon/components/components.go b/testscommon/components/components.go index ec360d4dd0d..d1dd94b9787 100644 --- a/testscommon/components/components.go +++ b/testscommon/components/components.go @@ -572,10 +572,25 @@ func GetProcessArgs( ) bootstrapComponentsFactoryArgs := GetBootStrapFactoryArgs() - bootstrapComponentsFactory, _ := bootstrapComp.NewBootstrapComponentsFactory(bootstrapComponentsFactoryArgs) - bootstrapComponents, _ := bootstrapComp.NewTestManagedBootstrapComponents(bootstrapComponentsFactory) - _ = bootstrapComponents.Create() - _ = bootstrapComponents.SetShardCoordinator(shardCoordinator) + bootstrapComponentsFactory, err := bootstrapComp.NewBootstrapComponentsFactory(bootstrapComponentsFactoryArgs) + if err != nil { + panic(err) + } + + bootstrapComponents, err := bootstrapComp.NewTestManagedBootstrapComponents(bootstrapComponentsFactory) + if err != nil { + panic(err) + } + + err = bootstrapComponents.Create() + if err != nil { + panic(err) + } + + err = bootstrapComponents.SetShardCoordinator(shardCoordinator) + if err != nil { + panic(err) + } args := processComp.ProcessComponentsFactoryArgs{ Config: testscommon.GetGeneralConfig(), @@ -888,9 +903,20 @@ func GetSovereignDataComponents(coreComponents factory.CoreComponentsHolder, sha } func createDataComponents(dataArgs dataComp.DataComponentsFactoryArgs) factory.DataComponentsHolder { - dataComponentsFactory, _ := dataComp.NewDataComponentsFactory(dataArgs) - dataComponents, _ := dataComp.NewManagedDataComponents(dataComponentsFactory) - _ = dataComponents.Create() + dataComponentsFactory, err := dataComp.NewDataComponentsFactory(dataArgs) + if err != nil { + panic(err) + } + + dataComponents, err := dataComp.NewManagedDataComponents(dataComponentsFactory) + if err != nil { + panic(err) + } + + err = dataComponents.Create() + if err != nil { + panic(err) + } return dataComponents } diff --git a/testscommon/dataRetriever/poolFactory.go b/testscommon/dataRetriever/poolFactory.go index a8f4374e800..71a27a718f2 100644 --- a/testscommon/dataRetriever/poolFactory.go +++ b/testscommon/dataRetriever/poolFactory.go @@ -40,11 +40,8 @@ func CreateTxPool(numShards uint32, selfShard uint32) (dataRetriever.ShardedData }, NumberOfShards: numShards, SelfShardID: selfShard, - TxGasHandler: &txcachemocks.TxGasHandlerMock{ - MinimumGasMove: 50000, - MinimumGasPrice: 200000000000, - GasProcessingDivisor: 100, - }, + TxGasHandler: txcachemocks.NewTxGasHandlerMock(), + Marshalizer: &marshal.GogoProtoMarshalizer{}, }, ) } diff --git a/testscommon/dataRetriever/poolsHolderMock.go b/testscommon/dataRetriever/poolsHolderMock.go index d3d30562954..75321b6854c 100644 --- a/testscommon/dataRetriever/poolsHolderMock.go +++ b/testscommon/dataRetriever/poolsHolderMock.go @@ -4,6 +4,7 @@ import ( "time" "github.com/multiversx/mx-chain-core-go/core/check" + "github.com/multiversx/mx-chain-core-go/marshal" "github.com/multiversx/mx-chain-go/config" "github.com/multiversx/mx-chain-go/dataRetriever" @@ -49,11 +50,8 @@ func NewPoolsHolderMock() *PoolsHolderMock { SizeInBytesPerSender: 10000000, Shards: 16, }, - TxGasHandler: &txcachemocks.TxGasHandlerMock{ - MinimumGasMove: 50000, - MinimumGasPrice: 200000000000, - GasProcessingDivisor: 100, - }, + TxGasHandler: txcachemocks.NewTxGasHandlerMock(), + Marshalizer: &marshal.GogoProtoMarshalizer{}, NumberOfShards: 1, }, ) diff --git a/testscommon/economicsmocks/economicsDataHandlerStub.go b/testscommon/economicsmocks/economicsDataHandlerStub.go index 4ef784c596f..a69206b12e6 100644 --- a/testscommon/economicsmocks/economicsDataHandlerStub.go +++ b/testscommon/economicsmocks/economicsDataHandlerStub.go @@ -47,6 +47,16 @@ type EconomicsHandlerStub struct { ComputeGasUsedAndFeeBasedOnRefundValueInEpochCalled func(tx data.TransactionWithFeeHandler, refundValue *big.Int, epoch uint32) (uint64, *big.Int) ComputeTxFeeBasedOnGasUsedInEpochCalled func(tx data.TransactionWithFeeHandler, gasUsed uint64, epoch uint32) *big.Int ComputeMoveBalanceFeeInEpochCalled func(tx data.TransactionWithFeeHandler, epoch uint32) *big.Int + ComputeGasUnitsFromRefundValueCalled func(tx data.TransactionWithFeeHandler, refundValue *big.Int, epoch uint32) uint64 +} + +// ComputeGasUnitsFromRefundValue - +func (e *EconomicsHandlerStub) ComputeGasUnitsFromRefundValue(tx data.TransactionWithFeeHandler, refundValue *big.Int, epoch uint32) uint64 { + if e.ComputeGasUnitsFromRefundValueCalled != nil { + return e.ComputeGasUnitsFromRefundValueCalled(tx, refundValue, epoch) + } + + return 0 } // ComputeFeeForProcessing - diff --git a/testscommon/economicsmocks/economicsHandlerMock.go b/testscommon/economicsmocks/economicsHandlerMock.go index b1e4321f389..304e86e37d2 100644 --- a/testscommon/economicsmocks/economicsHandlerMock.go +++ b/testscommon/economicsmocks/economicsHandlerMock.go @@ -49,6 +49,11 @@ type EconomicsHandlerMock struct { ComputeTxFeeBasedOnGasUsedInEpochCalled func(tx data.TransactionWithFeeHandler, gasUsed uint64, epoch uint32) *big.Int } +// ComputeGasUnitsFromRefundValue - +func (ehm *EconomicsHandlerMock) ComputeGasUnitsFromRefundValue(_ data.TransactionWithFeeHandler, _ *big.Int, _ uint32) uint64 { + return 0 +} + // LeaderPercentage - func (ehm *EconomicsHandlerMock) LeaderPercentage() float64 { return ehm.LeaderPercentageCalled() 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/state/accountsAdapterStub.go b/testscommon/state/accountsAdapterStub.go index 034d1f6ae26..70be0b5cc95 100644 --- a/testscommon/state/accountsAdapterStub.go +++ b/testscommon/state/accountsAdapterStub.go @@ -11,6 +11,8 @@ import ( var errNotImplemented = errors.New("not implemented") +var _ state.AccountsAdapter = (*AccountsStub)(nil) + // AccountsStub - type AccountsStub struct { GetExistingAccountCalled func(addressContainer []byte) (vmcommon.AccountHandler, 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] } }, ] diff --git a/testscommon/transactionCoordinatorMock.go b/testscommon/transactionCoordinatorMock.go index 651ff7dc54d..e870c67debd 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 @@ -62,9 +62,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/txCacherStub.go b/testscommon/txCacherStub.go index 783ac062b0c..68800ab9d2c 100644 --- a/testscommon/txCacherStub.go +++ b/testscommon/txCacherStub.go @@ -1,25 +1,21 @@ package testscommon -import "github.com/multiversx/mx-chain-go/storage/txcache" +import ( + "time" + + "github.com/multiversx/mx-chain-go/storage/txcache" +) type TxCacherStub struct { CacherStub - SelectTransactionsWithBandwidthCalled func(numRequested int, batchSizePerSender int, bandwidthPerSender uint64) []*txcache.WrappedTransaction - NotifyAccountNonceCalled func(accountKey []byte, nonce uint64) + SelectTransactionsCalled func(session txcache.SelectionSession, gasRequested uint64, maxNum int, selectionLoopMaximumDuration time.Duration) ([]*txcache.WrappedTransaction, uint64) } -// SelectTransactionsWithBandwidth - -func (tcs *TxCacherStub) SelectTransactionsWithBandwidth(numRequested int, batchSizePerSender int, bandwidthPerSender uint64) []*txcache.WrappedTransaction { - if tcs.SelectTransactionsWithBandwidthCalled != nil { - return tcs.SelectTransactionsWithBandwidthCalled(numRequested, batchSizePerSender, bandwidthPerSender) +// SelectTransactions - +func (tcs *TxCacherStub) SelectTransactions(session txcache.SelectionSession, gasRequested uint64, maxNum int, selectionLoopMaximumDuration time.Duration) ([]*txcache.WrappedTransaction, uint64) { + if tcs.SelectTransactionsCalled != nil { + return tcs.SelectTransactionsCalled(session, gasRequested, maxNum, selectionLoopMaximumDuration) } - return make([]*txcache.WrappedTransaction, 0) -} - -// NotifyAccountNonce - -func (tcs *TxCacherStub) NotifyAccountNonce(accountKey []byte, nonce uint64) { - if tcs.NotifyAccountNonceCalled != nil { - tcs.NotifyAccountNonceCalled(accountKey, nonce) - } + return make([]*txcache.WrappedTransaction, 0), 0 } diff --git a/testscommon/txProcessorMock.go b/testscommon/txProcessorMock.go index 82cd4fb4371..9a8e9dc80dd 100644 --- a/testscommon/txProcessorMock.go +++ b/testscommon/txProcessorMock.go @@ -5,14 +5,16 @@ import ( "github.com/multiversx/mx-chain-core-go/data/smartContractResult" "github.com/multiversx/mx-chain-core-go/data/transaction" - "github.com/multiversx/mx-chain-go/state" vmcommon "github.com/multiversx/mx-chain-vm-common-go" + + "github.com/multiversx/mx-chain-go/state" ) // TxProcessorMock - type TxProcessorMock struct { ProcessTransactionCalled func(transaction *transaction.Transaction) (vmcommon.ReturnCode, error) VerifyTransactionCalled func(tx *transaction.Transaction) error + VerifyGuardianCalled func(tx *transaction.Transaction, account state.UserAccountHandler) error GetSenderAndReceiverAccountsCalled func(tx *transaction.Transaction) (state.UserAccountHandler, state.UserAccountHandler, error) SetBalancesToTrieCalled func(accBalance map[string]*big.Int) (rootHash []byte, err error) ProcessSmartContractResultCalled func(scr *smartContractResult.SmartContractResult) (vmcommon.ReturnCode, error) @@ -36,6 +38,15 @@ func (etm *TxProcessorMock) VerifyTransaction(tx *transaction.Transaction) error return nil } +// VerifyGuardian - +func (etm *TxProcessorMock) VerifyGuardian(tx *transaction.Transaction, account state.UserAccountHandler) error { + if etm.VerifyGuardianCalled != nil { + return etm.VerifyGuardianCalled(tx, account) + } + + return nil +} + // GetSenderAndReceiverAccounts - func (etm *TxProcessorMock) GetSenderAndReceiverAccounts(tx *transaction.Transaction) (state.UserAccountHandler, state.UserAccountHandler, error) { if etm.GetSenderAndReceiverAccountsCalled != nil { diff --git a/testscommon/txProcessorStub.go b/testscommon/txProcessorStub.go index ed72d16b7d8..06885daf72a 100644 --- a/testscommon/txProcessorStub.go +++ b/testscommon/txProcessorStub.go @@ -2,14 +2,16 @@ package testscommon import ( "github.com/multiversx/mx-chain-core-go/data/transaction" - "github.com/multiversx/mx-chain-go/state" vmcommon "github.com/multiversx/mx-chain-vm-common-go" + + "github.com/multiversx/mx-chain-go/state" ) // TxProcessorStub - type TxProcessorStub struct { ProcessTransactionCalled func(transaction *transaction.Transaction) (vmcommon.ReturnCode, error) VerifyTransactionCalled func(tx *transaction.Transaction) error + VerifyGuardianCalled func(tx *transaction.Transaction, account state.UserAccountHandler) error GetSenderAndReceiverAccountsCalled func(tx *transaction.Transaction) (state.UserAccountHandler, state.UserAccountHandler, error) } @@ -31,6 +33,15 @@ func (tps *TxProcessorStub) VerifyTransaction(tx *transaction.Transaction) error return nil } +// VerifyGuardian - +func (tps *TxProcessorStub) VerifyGuardian(tx *transaction.Transaction, account state.UserAccountHandler) error { + if tps.VerifyGuardianCalled != nil { + return tps.VerifyGuardianCalled(tx, account) + } + + return nil +} + // GetSenderAndReceiverAccounts - func (tps *TxProcessorStub) GetSenderAndReceiverAccounts(tx *transaction.Transaction) (state.UserAccountHandler, state.UserAccountHandler, error) { if tps.GetSenderAndReceiverAccountsCalled != nil { 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) diff --git a/testscommon/txcachemocks/mempoolHostMock.go b/testscommon/txcachemocks/mempoolHostMock.go new file mode 100644 index 00000000000..3e9ecc995ec --- /dev/null +++ b/testscommon/txcachemocks/mempoolHostMock.go @@ -0,0 +1,41 @@ +package txcachemocks + +import ( + "math/big" + + "github.com/multiversx/mx-chain-core-go/data" +) + +// MempoolHostMock - +type MempoolHostMock struct { + ComputeTxFeeCalled func(tx data.TransactionWithFeeHandler) *big.Int + GetTransferredValueCalled func(tx data.TransactionHandler) *big.Int +} + +// NewMempoolHostMock - +func NewMempoolHostMock() *MempoolHostMock { + return &MempoolHostMock{} +} + +// ComputeTxFee - +func (mock *MempoolHostMock) ComputeTxFee(tx data.TransactionWithFeeHandler) *big.Int { + if mock.ComputeTxFeeCalled != nil { + return mock.ComputeTxFeeCalled(tx) + } + + return big.NewInt(0) +} + +// GetTransferredValue - +func (mock *MempoolHostMock) GetTransferredValue(tx data.TransactionHandler) *big.Int { + if mock.GetTransferredValueCalled != nil { + return mock.GetTransferredValueCalled(tx) + } + + return tx.GetValue() +} + +// IsInterfaceNil - +func (mock *MempoolHostMock) IsInterfaceNil() bool { + return mock == nil +} diff --git a/testscommon/txcachemocks/txCacheMock.go b/testscommon/txcachemocks/txCacheMock.go new file mode 100644 index 00000000000..c34db2d53b0 --- /dev/null +++ b/testscommon/txcachemocks/txCacheMock.go @@ -0,0 +1,220 @@ +package txcachemocks + +import "github.com/multiversx/mx-chain-storage-go/txcache" + +// TxCacheMock - +type TxCacheMock struct { + ClearCalled func() + PutCalled func(key []byte, value interface{}, sizeInBytes int) (evicted bool) + GetCalled func(key []byte) (value interface{}, ok bool) + HasCalled func(key []byte) bool + PeekCalled func(key []byte) (value interface{}, ok bool) + HasOrAddCalled func(key []byte, value interface{}, sizeInBytes int) (has, added bool) + RemoveCalled func(key []byte) + RemoveOldestCalled func() + KeysCalled func() [][]byte + LenCalled func() int + MaxSizeCalled func() int + RegisterHandlerCalled func(func(key []byte, value interface{})) + UnRegisterHandlerCalled func(id string) + CloseCalled func() error + + AddTxCalled func(tx *txcache.WrappedTransaction) (ok bool, added bool) + GetByTxHashCalled func(txHash []byte) (*txcache.WrappedTransaction, bool) + RemoveTxByHashCalled func(txHash []byte) bool + ImmunizeTxsAgainstEvictionCalled func(keys [][]byte) + ForEachTransactionCalled func(txcache.ForEachTransaction) + NumBytesCalled func() int + DiagnoseCalled func(deep bool) + GetTransactionsPoolForSenderCalled func(sender string) []*txcache.WrappedTransaction +} + +// NewTxCacheStub - +func NewTxCacheStub() *TxCacheMock { + return &TxCacheMock{} +} + +// Clear - +func (cache *TxCacheMock) Clear() { + if cache.ClearCalled != nil { + cache.ClearCalled() + } +} + +// Put - +func (cache *TxCacheMock) Put(key []byte, value interface{}, sizeInBytes int) (evicted bool) { + if cache.PutCalled != nil { + return cache.PutCalled(key, value, sizeInBytes) + } + + return false +} + +// Get - +func (cache *TxCacheMock) Get(key []byte) (value interface{}, ok bool) { + if cache.GetCalled != nil { + return cache.GetCalled(key) + } + + return nil, false +} + +// Has - +func (cache *TxCacheMock) Has(key []byte) bool { + if cache.HasCalled != nil { + return cache.HasCalled(key) + } + + return false +} + +// Peek - +func (cache *TxCacheMock) Peek(key []byte) (value interface{}, ok bool) { + if cache.PeekCalled != nil { + return cache.PeekCalled(key) + } + + return nil, false +} + +// HasOrAdd - +func (cache *TxCacheMock) HasOrAdd(key []byte, value interface{}, sizeInBytes int) (has, added bool) { + if cache.HasOrAddCalled != nil { + return cache.HasOrAddCalled(key, value, sizeInBytes) + } + + return false, false +} + +// Remove - +func (cache *TxCacheMock) Remove(key []byte) { + if cache.RemoveCalled != nil { + cache.RemoveCalled(key) + } +} + +// Keys - +func (cache *TxCacheMock) Keys() [][]byte { + if cache.KeysCalled != nil { + return cache.KeysCalled() + } + + return make([][]byte, 0) +} + +// Len - +func (cache *TxCacheMock) Len() int { + if cache.LenCalled != nil { + return cache.LenCalled() + } + + return 0 +} + +// SizeInBytesContained - +func (cache *TxCacheMock) SizeInBytesContained() uint64 { + return 0 +} + +// MaxSize - +func (cache *TxCacheMock) MaxSize() int { + if cache.MaxSizeCalled != nil { + return cache.MaxSizeCalled() + } + + return 0 +} + +// RegisterHandler - +func (cache *TxCacheMock) RegisterHandler(handler func(key []byte, value interface{}), _ string) { + if cache.RegisterHandlerCalled != nil { + cache.RegisterHandlerCalled(handler) + } +} + +// UnRegisterHandler - +func (cache *TxCacheMock) UnRegisterHandler(id string) { + if cache.UnRegisterHandlerCalled != nil { + cache.UnRegisterHandlerCalled(id) + } +} + +// Close - +func (cache *TxCacheMock) Close() error { + if cache.CloseCalled != nil { + return cache.CloseCalled() + } + + return nil +} + +// AddTx - +func (cache *TxCacheMock) AddTx(tx *txcache.WrappedTransaction) (ok bool, added bool) { + if cache.AddTxCalled != nil { + return cache.AddTxCalled(tx) + } + + return false, false +} + +// GetByTxHash - +func (cache *TxCacheMock) GetByTxHash(txHash []byte) (*txcache.WrappedTransaction, bool) { + if cache.GetByTxHashCalled != nil { + return cache.GetByTxHashCalled(txHash) + } + + return nil, false +} + +// RemoveTxByHash - +func (cache *TxCacheMock) RemoveTxByHash(txHash []byte) bool { + if cache.RemoveTxByHashCalled != nil { + return cache.RemoveTxByHashCalled(txHash) + } + + return false +} + +// ImmunizeTxsAgainstEviction - +func (cache *TxCacheMock) ImmunizeTxsAgainstEviction(keys [][]byte) { + if cache.ImmunizeTxsAgainstEvictionCalled != nil { + cache.ImmunizeTxsAgainstEvictionCalled(keys) + } +} + +// ForEachTransaction - +func (cache *TxCacheMock) ForEachTransaction(fn txcache.ForEachTransaction) { + if cache.ForEachTransactionCalled != nil { + cache.ForEachTransactionCalled(fn) + } +} + +// NumBytes - +func (cache *TxCacheMock) NumBytes() int { + if cache.NumBytesCalled != nil { + return cache.NumBytesCalled() + } + + return 0 +} + +// Diagnose - +func (cache *TxCacheMock) Diagnose(deep bool) { + if cache.DiagnoseCalled != nil { + cache.DiagnoseCalled(deep) + } +} + +// GetTransactionsPoolForSender - +func (cache *TxCacheMock) GetTransactionsPoolForSender(sender string) []*txcache.WrappedTransaction { + if cache.GetTransactionsPoolForSenderCalled != nil { + return cache.GetTransactionsPoolForSenderCalled(sender) + } + + return nil +} + +// IsInterfaceNil - +func (cache *TxCacheMock) IsInterfaceNil() bool { + return cache == nil +} diff --git a/testscommon/txcachemocks/txGasHandlerMock.go b/testscommon/txcachemocks/txGasHandlerMock.go index f9da5dfad09..a624e29372a 100644 --- a/testscommon/txcachemocks/txGasHandlerMock.go +++ b/testscommon/txcachemocks/txGasHandlerMock.go @@ -1,56 +1,84 @@ package txcachemocks import ( + "math/big" + + "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-core-go/data" ) // TxGasHandler - type TxGasHandler interface { - SplitTxGasInCategories(tx data.TransactionWithFeeHandler) (uint64, uint64) - GasPriceForProcessing(tx data.TransactionWithFeeHandler) uint64 - GasPriceForMove(tx data.TransactionWithFeeHandler) uint64 MinGasPrice() uint64 - MinGasLimit() uint64 - MinGasPriceForProcessing() uint64 + MaxGasLimitPerTx() uint64 + ComputeTxFee(tx data.TransactionWithFeeHandler) *big.Int IsInterfaceNil() bool } // TxGasHandlerMock - type TxGasHandlerMock struct { - MinimumGasMove uint64 - MinimumGasPrice uint64 - GasProcessingDivisor uint64 + minGasLimit uint64 + minGasPrice uint64 + maxGasLimitPerTx uint64 + gasPerDataByte uint64 + gasPriceModifier float64 } -// SplitTxGasInCategories - -func (ghm *TxGasHandlerMock) SplitTxGasInCategories(tx data.TransactionWithFeeHandler) (uint64, uint64) { - moveGas := ghm.MinimumGasMove - return moveGas, tx.GetGasLimit() - moveGas +// NewTxGasHandlerMock - +func NewTxGasHandlerMock() *TxGasHandlerMock { + return &TxGasHandlerMock{ + minGasLimit: 50000, + minGasPrice: 1000000000, + maxGasLimitPerTx: 600000000, + gasPerDataByte: 1500, + gasPriceModifier: 0.01, + } } -// GasPriceForProcessing - -func (ghm *TxGasHandlerMock) GasPriceForProcessing(tx data.TransactionWithFeeHandler) uint64 { - return tx.GetGasPrice() / ghm.GasProcessingDivisor +// WithMinGasLimit - +func (ghm *TxGasHandlerMock) WithMinGasLimit(minGasLimit uint64) *TxGasHandlerMock { + ghm.minGasLimit = minGasLimit + return ghm } -// GasPriceForMove - -func (ghm *TxGasHandlerMock) GasPriceForMove(tx data.TransactionWithFeeHandler) uint64 { - return tx.GetGasPrice() +// WithMinGasPrice - +func (ghm *TxGasHandlerMock) WithMinGasPrice(minGasPrice uint64) *TxGasHandlerMock { + ghm.minGasPrice = minGasPrice + return ghm } // MinGasPrice - func (ghm *TxGasHandlerMock) MinGasPrice() uint64 { - return ghm.MinimumGasPrice + return ghm.minGasPrice +} + +// WithGasPriceModifier - +func (ghm *TxGasHandlerMock) WithGasPriceModifier(gasPriceModifier float64) *TxGasHandlerMock { + ghm.gasPriceModifier = gasPriceModifier + return ghm } -// MinGasLimit - -func (ghm *TxGasHandlerMock) MinGasLimit() uint64 { - return ghm.MinimumGasMove +// MaxGasLimitPerTx - +func (ghm *TxGasHandlerMock) MaxGasLimitPerTx() uint64 { + return ghm.maxGasLimitPerTx } -// MinGasPriceForProcessing - -func (ghm *TxGasHandlerMock) MinGasPriceForProcessing() uint64 { - return ghm.MinimumGasPrice / ghm.GasProcessingDivisor +// ComputeTxFee - +func (ghm *TxGasHandlerMock) ComputeTxFee(tx data.TransactionWithFeeHandler) *big.Int { + dataLength := uint64(len(tx.GetData())) + gasPriceForMovement := tx.GetGasPrice() + gasPriceForProcessing := uint64(float64(gasPriceForMovement) * ghm.gasPriceModifier) + + gasLimitForMovement := ghm.minGasLimit + dataLength*ghm.gasPerDataByte + if tx.GetGasLimit() < gasLimitForMovement { + return big.NewInt(0) + } + + gasLimitForProcessing := tx.GetGasLimit() - gasLimitForMovement + feeForMovement := core.SafeMul(gasPriceForMovement, gasLimitForMovement) + feeForProcessing := core.SafeMul(gasPriceForProcessing, gasLimitForProcessing) + fee := big.NewInt(0).Add(feeForMovement, feeForProcessing) + return fee } // IsInterfaceNil - diff --git a/trie/patriciaMerkleTrie.go b/trie/patriciaMerkleTrie.go index 70df549011f..da9eb87a65f 100644 --- a/trie/patriciaMerkleTrie.go +++ b/trie/patriciaMerkleTrie.go @@ -11,13 +11,14 @@ import ( "github.com/multiversx/mx-chain-core-go/core/check" "github.com/multiversx/mx-chain-core-go/hashing" "github.com/multiversx/mx-chain-core-go/marshal" + logger "github.com/multiversx/mx-chain-logger-go" + vmcommon "github.com/multiversx/mx-chain-vm-common-go" + "github.com/multiversx/mx-chain-go/common" "github.com/multiversx/mx-chain-go/dataRetriever" "github.com/multiversx/mx-chain-go/errors" "github.com/multiversx/mx-chain-go/trie/keyBuilder" "github.com/multiversx/mx-chain-go/trie/statistics" - logger "github.com/multiversx/mx-chain-logger-go" - vmcommon "github.com/multiversx/mx-chain-vm-common-go" ) var log = logger.GetOrCreate("trie") @@ -118,10 +119,7 @@ func (tr *patriciaMerkleTrie) Update(key, value []byte) error { tr.mutOperation.Lock() defer tr.mutOperation.Unlock() - log.Trace("update trie", - "key", hex.EncodeToString(key), - "val", hex.EncodeToString(value), - ) + log.Trace("update trie", "key", key, "val", value) return tr.update(key, value, core.NotSpecified) } @@ -131,11 +129,7 @@ func (tr *patriciaMerkleTrie) UpdateWithVersion(key []byte, value []byte, versio tr.mutOperation.Lock() defer tr.mutOperation.Unlock() - log.Trace("update trie with version", - "key", hex.EncodeToString(key), - "val", hex.EncodeToString(value), - "version", version, - ) + log.Trace("update trie with version", "key", key, "val", value, "version", version) return tr.update(key, value, version) } diff --git a/trie/patriciaMerkleTrie_test.go b/trie/patriciaMerkleTrie_test.go index 4612b8ee331..8a02a8edcd9 100644 --- a/trie/patriciaMerkleTrie_test.go +++ b/trie/patriciaMerkleTrie_test.go @@ -17,6 +17,10 @@ import ( "github.com/multiversx/mx-chain-core-go/hashing" "github.com/multiversx/mx-chain-core-go/hashing/keccak" "github.com/multiversx/mx-chain-core-go/marshal" + vmcommon "github.com/multiversx/mx-chain-vm-common-go" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/multiversx/mx-chain-go/common" "github.com/multiversx/mx-chain-go/common/errChan" "github.com/multiversx/mx-chain-go/common/holders" @@ -28,9 +32,6 @@ import ( "github.com/multiversx/mx-chain-go/trie" "github.com/multiversx/mx-chain-go/trie/keyBuilder" "github.com/multiversx/mx-chain-go/trie/mock" - vmcommon "github.com/multiversx/mx-chain-vm-common-go" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) var emptyTrieHash = make([]byte, 32) @@ -1708,3 +1709,28 @@ func BenchmarkPatriciaMerkleTrie_RootHashAfterChanging30000NodesInBatchesOf200(b } } } + +func BenchmarkPatriciaMerkleTrie_Update(b *testing.B) { + tr := emptyTrie() + hsh := keccak.NewKeccak() + + nrValuesInTrie := 2000000 + values := make([][]byte, nrValuesInTrie) + + for i := 0; i < nrValuesInTrie; i++ { + key := hsh.Compute(strconv.Itoa(i)) + value := append(key, []byte(strconv.Itoa(i))...) + + _ = tr.Update(key, value) + values[i] = key + } + _ = tr.Commit() + + b.ResetTimer() + + for i := 0; i < b.N; i++ { + for j := 0; j < nrValuesInTrie; j++ { + _ = tr.Update(values[j], values[j]) + } + } +}