diff --git a/cmd/scCallsExecutor/config/config.toml b/cmd/scCallsExecutor/config/config.toml index 9f60880c..7b13c18d 100644 --- a/cmd/scCallsExecutor/config/config.toml +++ b/cmd/scCallsExecutor/config/config.toml @@ -1,17 +1,24 @@ -ScProxyBech32Addresses = [ - "erd1qqqqqqqqqqqqqpgqnef5f5aq32d63kljld8w5vnvz4gk5sy9hrrq2ld08s", -] -ExtraGasToExecute = 60000000 # this value allow the SC calls without provided gas limit to be refunded -MaxGasLimitToUse = 249999999 # this is a safe max gas limit to use both intra-shard & cross-shard -GasLimitForOutOfGasTransactions = 30000000 # this value will be used when a transaction specified a gas limit > 249999999 -NetworkAddress = "http://127.0.0.1:8085" -ProxyMaxNoncesDelta = 7 -ProxyFinalityCheck = true -ProxyCacherExpirationSeconds = 600 -ProxyRestAPIEntityType = "proxy" -IntervalToResendTxsInSeconds = 60 -PrivateKeyFile = "keys/multiversx.pem" -PollingIntervalInMillis = 6000 +[General] + ScProxyBech32Addresses = [ + "erd1qqqqqqqqqqqqqpgqnef5f5aq32d63kljld8w5vnvz4gk5sy9hrrq2ld08s", + ] + NetworkAddress = "http://127.0.0.1:8085" + ProxyMaxNoncesDelta = 7 + ProxyFinalityCheck = true + ProxyCacherExpirationSeconds = 600 + ProxyRestAPIEntityType = "proxy" + IntervalToResendTxsInSeconds = 60 + PrivateKeyFile = "keys/multiversx.pem" + +[ScCallsExecutor] + ExtraGasToExecute = 60000000 # this value allow the SC calls without provided gas limit to be refunded + MaxGasLimitToUse = 249999999 # this is a safe max gas limit to use both intra-shard & cross-shard + GasLimitForOutOfGasTransactions = 30000000 # this value will be used when a transaction specified a gas limit > 249999999 + PollingIntervalInMillis = 6000 + +[RefundExecutor] + GasToExecute = 20000000 + PollingIntervalInMillis = 6000 [Filter] AllowedEthAddresses = ["*"] # execute SC calls from all ETH addresses diff --git a/cmd/scCallsExecutor/main.go b/cmd/scCallsExecutor/main.go index 7b929780..7b13a3da 100644 --- a/cmd/scCallsExecutor/main.go +++ b/cmd/scCallsExecutor/main.go @@ -94,38 +94,20 @@ func startExecutor(ctx *cli.Context, version string) error { } if ctx.IsSet(networkAddress.Name) { - cfg.NetworkAddress = ctx.GlobalString(networkAddress.Name) - log.Info("using flag-defined network address", "address", cfg.NetworkAddress) + cfg.General.NetworkAddress = ctx.GlobalString(networkAddress.Name) + log.Info("using flag-defined network address", "address", cfg.General.NetworkAddress) } if ctx.IsSet(privateKeyFile.Name) { - cfg.PrivateKeyFile = ctx.GlobalString(privateKeyFile.Name) - log.Info("using flag-defined private key file", "filename", cfg.PrivateKeyFile) + cfg.General.PrivateKeyFile = ctx.GlobalString(privateKeyFile.Name) + log.Info("using flag-defined private key file", "filename", cfg.General.PrivateKeyFile) } - if len(cfg.NetworkAddress) == 0 { + if len(cfg.General.NetworkAddress) == 0 { return fmt.Errorf("empty NetworkAddress in config file") } - args := config.ScCallsModuleConfig{ - ScProxyBech32Addresses: cfg.ScProxyBech32Addresses, - ExtraGasToExecute: cfg.ExtraGasToExecute, - MaxGasLimitToUse: cfg.MaxGasLimitToUse, - GasLimitForOutOfGasTransactions: cfg.GasLimitForOutOfGasTransactions, - NetworkAddress: cfg.NetworkAddress, - ProxyMaxNoncesDelta: cfg.ProxyMaxNoncesDelta, - ProxyFinalityCheck: cfg.ProxyFinalityCheck, - ProxyCacherExpirationSeconds: cfg.ProxyCacherExpirationSeconds, - ProxyRestAPIEntityType: cfg.ProxyRestAPIEntityType, - IntervalToResendTxsInSeconds: cfg.IntervalToResendTxsInSeconds, - PrivateKeyFile: cfg.PrivateKeyFile, - PollingIntervalInMillis: cfg.PollingIntervalInMillis, - Filter: cfg.Filter, - Logs: cfg.Logs, - TransactionChecks: cfg.TransactionChecks, - } - chCloseApp := make(chan struct{}, 1) - scCallsExecutor, err := module.NewScCallsModule(args, log, chCloseApp) + scCallsExecutor, err := module.NewScCallsModule(cfg, log, chCloseApp) if err != nil { return err } diff --git a/config/config.go b/config/config.go index a91ebfda..0b053550 100644 --- a/config/config.go +++ b/config/config.go @@ -192,21 +192,38 @@ type PendingOperationsFilterConfig struct { // ScCallsModuleConfig will hold the settings for the SC calls module type ScCallsModuleConfig struct { - ScProxyBech32Addresses []string + General GeneralScCallsModuleConfig + ScCallsExecutor ScCallsExecutorConfig + RefundExecutor RefundExecutorConfig + Filter PendingOperationsFilterConfig + Logs LogsConfig + TransactionChecks TransactionChecksConfig +} + +// GeneralScCallsModuleConfig will hold the general settings for the SC calls module +type GeneralScCallsModuleConfig struct { + ScProxyBech32Addresses []string + NetworkAddress string + ProxyMaxNoncesDelta int + ProxyFinalityCheck bool + ProxyCacherExpirationSeconds uint64 + ProxyRestAPIEntityType string + IntervalToResendTxsInSeconds uint64 + PrivateKeyFile string +} + +// ScCallsExecutorConfig will hold the settings for the SC calls executor +type ScCallsExecutorConfig struct { ExtraGasToExecute uint64 MaxGasLimitToUse uint64 GasLimitForOutOfGasTransactions uint64 - NetworkAddress string - ProxyMaxNoncesDelta int - ProxyFinalityCheck bool - ProxyCacherExpirationSeconds uint64 - ProxyRestAPIEntityType string - IntervalToResendTxsInSeconds uint64 - PrivateKeyFile string PollingIntervalInMillis uint64 - Filter PendingOperationsFilterConfig - Logs LogsConfig - TransactionChecks TransactionChecksConfig +} + +// RefundExecutorConfig will hold the settings for the refund executor +type RefundExecutorConfig struct { + GasToExecute uint64 + PollingIntervalInMillis uint64 } // TransactionChecksConfig will hold the setting for how to handle the transaction execution diff --git a/config/tomlConfigs_test.go b/config/tomlConfigs_test.go index 057ef77a..659e930f 100644 --- a/config/tomlConfigs_test.go +++ b/config/tomlConfigs_test.go @@ -405,21 +405,29 @@ func TestScCallsExecutorConfigs(t *testing.T) { t.Parallel() expectedConfig := ScCallsModuleConfig{ - ScProxyBech32Addresses: []string{ - "erd1qqqqqqqqqqqqqpgqnef5f5aq32d63kljld8w5vnvz4gk5sy9hrrq2ld08s", - "erd1qqqqqqqqqqqqqpgqzyuaqg3dl7rqlkudrsnm5ek0j3a97qevd8sszj0glf", + General: GeneralScCallsModuleConfig{ + ScProxyBech32Addresses: []string{ + "erd1qqqqqqqqqqqqqpgqnef5f5aq32d63kljld8w5vnvz4gk5sy9hrrq2ld08s", + "erd1qqqqqqqqqqqqqpgqzyuaqg3dl7rqlkudrsnm5ek0j3a97qevd8sszj0glf", + }, + NetworkAddress: "127.0.0.1:8085", + ProxyMaxNoncesDelta: 7, + ProxyFinalityCheck: true, + ProxyCacherExpirationSeconds: 600, + ProxyRestAPIEntityType: "observer", + IntervalToResendTxsInSeconds: 60, + PrivateKeyFile: "keys/multiversx.pem", + }, + ScCallsExecutor: ScCallsExecutorConfig{ + ExtraGasToExecute: 50000000, + MaxGasLimitToUse: 249999999, + GasLimitForOutOfGasTransactions: 30000000, + PollingIntervalInMillis: 6000, + }, + RefundExecutor: RefundExecutorConfig{ + GasToExecute: 20000000, + PollingIntervalInMillis: 6000, }, - ExtraGasToExecute: 50000000, - MaxGasLimitToUse: 249999999, - GasLimitForOutOfGasTransactions: 30000000, - NetworkAddress: "127.0.0.1:8085", - ProxyMaxNoncesDelta: 7, - ProxyFinalityCheck: true, - ProxyCacherExpirationSeconds: 600, - ProxyRestAPIEntityType: "observer", - IntervalToResendTxsInSeconds: 60, - PrivateKeyFile: "keys/multiversx.pem", - PollingIntervalInMillis: 6000, Filter: PendingOperationsFilterConfig{ AllowedEthAddresses: []string{"*"}, AllowedMvxAddresses: []string{"*"}, @@ -439,21 +447,28 @@ func TestScCallsExecutorConfigs(t *testing.T) { } testString := ` -ScProxyBech32Addresses = [ - "erd1qqqqqqqqqqqqqpgqnef5f5aq32d63kljld8w5vnvz4gk5sy9hrrq2ld08s", - "erd1qqqqqqqqqqqqqpgqzyuaqg3dl7rqlkudrsnm5ek0j3a97qevd8sszj0glf", -] -ExtraGasToExecute = 50000000 -MaxGasLimitToUse = 249999999 # this is a safe max gas limit to use both intra-shard & cross-shard -GasLimitForOutOfGasTransactions = 30000000 # this value will be used when a transaction specified a gas limit > 249999999 -NetworkAddress = "127.0.0.1:8085" -ProxyMaxNoncesDelta = 7 -ProxyFinalityCheck = true -ProxyCacherExpirationSeconds = 600 -ProxyRestAPIEntityType = "observer" -IntervalToResendTxsInSeconds = 60 -PrivateKeyFile = "keys/multiversx.pem" -PollingIntervalInMillis = 6000 +[General] + ScProxyBech32Addresses = [ + "erd1qqqqqqqqqqqqqpgqnef5f5aq32d63kljld8w5vnvz4gk5sy9hrrq2ld08s", + "erd1qqqqqqqqqqqqqpgqzyuaqg3dl7rqlkudrsnm5ek0j3a97qevd8sszj0glf", + ] + NetworkAddress = "127.0.0.1:8085" + ProxyMaxNoncesDelta = 7 + ProxyFinalityCheck = true + ProxyCacherExpirationSeconds = 600 + ProxyRestAPIEntityType = "observer" + IntervalToResendTxsInSeconds = 60 + PrivateKeyFile = "keys/multiversx.pem" + +[ScCallsExecutor] + ExtraGasToExecute = 50000000 + MaxGasLimitToUse = 249999999 # this is a safe max gas limit to use both intra-shard & cross-shard + GasLimitForOutOfGasTransactions = 30000000 # this value will be used when a transaction specified a gas limit > 249999999 + PollingIntervalInMillis = 6000 + +[RefundExecutor] + GasToExecute = 20000000 + PollingIntervalInMillis = 6000 [Filter] AllowedEthAddresses = ["*"] # execute SC calls from all ETH addresses diff --git a/executors/multiversx/errors.go b/executors/multiversx/errors.go index 9c7ee755..dbd7ec03 100644 --- a/executors/multiversx/errors.go +++ b/executors/multiversx/errors.go @@ -16,4 +16,5 @@ var ( errTransactionFailed = errors.New("transaction failed") errGasLimitIsLessThanAbsoluteMinimum = errors.New("provided gas limit is less than absolute minimum required") errEmptyListOfBridgeSCProxy = errors.New("the bridge SC proxy addresses list is empty") + errNilTransactionExecutor = errors.New("nil transaction executor") ) diff --git a/executors/multiversx/interface.go b/executors/multiversx/interface.go index c83f1cda..e09d3901 100644 --- a/executors/multiversx/interface.go +++ b/executors/multiversx/interface.go @@ -45,3 +45,17 @@ type Codec interface { ExtractGasLimitFromRawCallData(buff []byte) (uint64, error) IsInterfaceNil() bool } + +// TransactionExecutor defines the operations of an entity able to send executable transactions +type TransactionExecutor interface { + ExecuteTransaction( + ctx context.Context, + networkConfig *data.NetworkConfig, + receiver string, + transactionType string, + gasLimit uint64, + dataBytes []byte, + ) error + GetNumSentTransaction() uint32 + IsInterfaceNil() bool +} diff --git a/executors/multiversx/module/scCallsModule.go b/executors/multiversx/module/scCallsModule.go index 4d1733bd..e753c47b 100644 --- a/executors/multiversx/module/scCallsModule.go +++ b/executors/multiversx/module/scCallsModule.go @@ -36,13 +36,13 @@ func NewScCallsModule(cfg config.ScCallsModuleConfig, log logger.Logger, chClose } argsProxy := blockchain.ArgsProxy{ - ProxyURL: cfg.NetworkAddress, + ProxyURL: cfg.General.NetworkAddress, SameScState: false, ShouldBeSynced: false, - FinalityCheck: cfg.ProxyFinalityCheck, - AllowedDeltaToFinal: cfg.ProxyMaxNoncesDelta, - CacheExpirationTime: time.Second * time.Duration(cfg.ProxyCacherExpirationSeconds), - EntityType: sdkCore.RestAPIEntityType(cfg.ProxyRestAPIEntityType), + FinalityCheck: cfg.General.ProxyFinalityCheck, + AllowedDeltaToFinal: cfg.General.ProxyMaxNoncesDelta, + CacheExpirationTime: time.Second * time.Duration(cfg.General.ProxyCacherExpirationSeconds), + EntityType: sdkCore.RestAPIEntityType(cfg.General.ProxyRestAPIEntityType), } proxy, err := blockchain.NewProxy(argsProxy) @@ -54,7 +54,7 @@ func NewScCallsModule(cfg config.ScCallsModuleConfig, log logger.Logger, chClose argNonceHandler := nonceHandlerV2.ArgsNonceTransactionsHandlerV2{ Proxy: proxy, - IntervalToResend: time.Second * time.Duration(cfg.IntervalToResendTxsInSeconds), + IntervalToResend: time.Second * time.Duration(cfg.General.IntervalToResendTxsInSeconds), } module.nonceTxsHandler, err = nonceHandlerV2.NewNonceTransactionHandlerV2(argNonceHandler) if err != nil { @@ -62,7 +62,7 @@ func NewScCallsModule(cfg config.ScCallsModuleConfig, log logger.Logger, chClose } wallet := interactors.NewWallet() - multiversXPrivateKeyBytes, err := wallet.LoadPrivateKeyFromPemFile(cfg.PrivateKeyFile) + multiversXPrivateKeyBytes, err := wallet.LoadPrivateKeyFromPemFile(cfg.General.PrivateKeyFile) if err != nil { return nil, err } @@ -72,20 +72,30 @@ func NewScCallsModule(cfg config.ScCallsModuleConfig, log logger.Logger, chClose return nil, err } + argsTxExecutor := multiversx.ArgsTransactionExecutor{ + Proxy: proxy, + Log: log, + NonceTxHandler: module.nonceTxsHandler, + PrivateKey: privateKey, + SingleSigner: singleSigner, + TransactionChecks: cfg.TransactionChecks, + CloseAppChan: chCloseApp, + } + + transactionExecutor, err := multiversx.NewTransactionExecutor(argsTxExecutor) + if err != nil { + return nil, err + } + argsExecutor := multiversx.ArgsScCallExecutor{ - ScProxyBech32Addresses: cfg.ScProxyBech32Addresses, - Proxy: proxy, - Codec: &parsers.MultiversxCodec{}, - Filter: filter, - Log: log, - ExtraGasToExecute: cfg.ExtraGasToExecute, - MaxGasLimitToUse: cfg.MaxGasLimitToUse, - GasLimitForOutOfGasTransactions: cfg.GasLimitForOutOfGasTransactions, - NonceTxHandler: module.nonceTxsHandler, - PrivateKey: privateKey, - SingleSigner: singleSigner, - CloseAppChan: chCloseApp, - TransactionChecks: cfg.TransactionChecks, + ScProxyBech32Addresses: cfg.General.ScProxyBech32Addresses, + TransactionExecutor: transactionExecutor, + Proxy: proxy, + Codec: &parsers.MultiversxCodec{}, + Filter: filter, + Log: log, + ExecutorConfig: cfg.ScCallsExecutor, + TransactionChecks: cfg.TransactionChecks, } module.executorInstance, err = multiversx.NewScCallExecutor(argsExecutor) if err != nil { @@ -95,8 +105,8 @@ func NewScCallsModule(cfg config.ScCallsModuleConfig, log logger.Logger, chClose argsPollingHandler := polling.ArgsPollingHandler{ Log: log, Name: "MultiversX SC calls", - PollingInterval: time.Duration(cfg.PollingIntervalInMillis) * time.Millisecond, - PollingWhenError: time.Duration(cfg.PollingIntervalInMillis) * time.Millisecond, + PollingInterval: time.Duration(cfg.ScCallsExecutor.PollingIntervalInMillis) * time.Millisecond, + PollingWhenError: time.Duration(cfg.ScCallsExecutor.PollingIntervalInMillis) * time.Millisecond, Executor: module.executorInstance, } diff --git a/executors/multiversx/module/scCallsModule_test.go b/executors/multiversx/module/scCallsModule_test.go index cb5f7467..097efeca 100644 --- a/executors/multiversx/module/scCallsModule_test.go +++ b/executors/multiversx/module/scCallsModule_test.go @@ -11,20 +11,24 @@ import ( func createTestConfigs() config.ScCallsModuleConfig { return config.ScCallsModuleConfig{ - ScProxyBech32Addresses: []string{ - "erd1qqqqqqqqqqqqqpgqgftcwj09u0nhmskrw7xxqcqh8qmzwyexd8ss7ftcxx", + General: config.GeneralScCallsModuleConfig{ + ScProxyBech32Addresses: []string{ + "erd1qqqqqqqqqqqqqpgqgftcwj09u0nhmskrw7xxqcqh8qmzwyexd8ss7ftcxx", + }, + NetworkAddress: "http://127.0.0.1:8079", + ProxyMaxNoncesDelta: 5, + ProxyFinalityCheck: false, + ProxyCacherExpirationSeconds: 60, + ProxyRestAPIEntityType: string(sdkCore.ObserverNode), + IntervalToResendTxsInSeconds: 1, + PrivateKeyFile: "testdata/grace.pem", + }, + ScCallsExecutor: config.ScCallsExecutorConfig{ + ExtraGasToExecute: 6000000, + MaxGasLimitToUse: 249999999, + GasLimitForOutOfGasTransactions: 30000000, + PollingIntervalInMillis: 10000, }, - ExtraGasToExecute: 6000000, - MaxGasLimitToUse: 249999999, - GasLimitForOutOfGasTransactions: 30000000, - NetworkAddress: "http://127.0.0.1:8079", - ProxyMaxNoncesDelta: 5, - ProxyFinalityCheck: false, - ProxyCacherExpirationSeconds: 60, - ProxyRestAPIEntityType: string(sdkCore.ObserverNode), - IntervalToResendTxsInSeconds: 1, - PrivateKeyFile: "testdata/grace.pem", - PollingIntervalInMillis: 10000, Filter: config.PendingOperationsFilterConfig{ DeniedEthAddresses: nil, AllowedEthAddresses: []string{"*"}, @@ -54,7 +58,7 @@ func TestNewScCallsModule(t *testing.T) { t.Parallel() cfg := createTestConfigs() - cfg.ProxyCacherExpirationSeconds = 0 + cfg.General.ProxyCacherExpirationSeconds = 0 module, err := NewScCallsModule(cfg, &testsCommon.LoggerStub{}, nil) assert.NotNil(t, err) @@ -65,7 +69,7 @@ func TestNewScCallsModule(t *testing.T) { t.Parallel() cfg := createTestConfigs() - cfg.IntervalToResendTxsInSeconds = 0 + cfg.General.IntervalToResendTxsInSeconds = 0 module, err := NewScCallsModule(cfg, &testsCommon.LoggerStub{}, nil) assert.NotNil(t, err) @@ -76,7 +80,7 @@ func TestNewScCallsModule(t *testing.T) { t.Parallel() cfg := createTestConfigs() - cfg.PrivateKeyFile = "" + cfg.General.PrivateKeyFile = "" module, err := NewScCallsModule(cfg, &testsCommon.LoggerStub{}, nil) assert.NotNil(t, err) @@ -86,7 +90,7 @@ func TestNewScCallsModule(t *testing.T) { t.Parallel() cfg := createTestConfigs() - cfg.PollingIntervalInMillis = 0 + cfg.ScCallsExecutor.PollingIntervalInMillis = 0 module, err := NewScCallsModule(cfg, &testsCommon.LoggerStub{}, nil) assert.NotNil(t, err) @@ -127,7 +131,7 @@ func TestNewScCallsModule(t *testing.T) { t.Parallel() cfg := createTestConfigs() - cfg.ScProxyBech32Addresses = append(cfg.ScProxyBech32Addresses, "erd1qqqqqqqqqqqqqpgqzyuaqg3dl7rqlkudrsnm5ek0j3a97qevd8sszj0glf") + cfg.General.ScProxyBech32Addresses = append(cfg.General.ScProxyBech32Addresses, "erd1qqqqqqqqqqqqqpgqzyuaqg3dl7rqlkudrsnm5ek0j3a97qevd8sszj0glf") cfg.TransactionChecks.CheckTransactionResults = true cfg.TransactionChecks.TimeInSecondsBetweenChecks = 1 cfg.TransactionChecks.ExecutionTimeoutInSeconds = 1 diff --git a/executors/multiversx/scCallsExecutor.go b/executors/multiversx/scCallsExecutor.go index fb81848d..99f303a3 100644 --- a/executors/multiversx/scCallsExecutor.go +++ b/executors/multiversx/scCallsExecutor.go @@ -2,23 +2,17 @@ package multiversx import ( "context" - "encoding/hex" - "encoding/json" "fmt" "math/big" "strings" - "sync/atomic" "time" "github.com/multiversx/mx-bridge-eth-go/config" "github.com/multiversx/mx-bridge-eth-go/errors" "github.com/multiversx/mx-bridge-eth-go/parsers" "github.com/multiversx/mx-chain-core-go/core/check" - "github.com/multiversx/mx-chain-core-go/data/transaction" - crypto "github.com/multiversx/mx-chain-crypto-go" logger "github.com/multiversx/mx-chain-logger-go" "github.com/multiversx/mx-sdk-go/builders" - "github.com/multiversx/mx-sdk-go/core" "github.com/multiversx/mx-sdk-go/data" ) @@ -27,87 +21,62 @@ const ( okCodeAfterExecution = "ok" scProxyCallFunction = "execute" contractMaxGasLimit = 249999999 + scCallTxType = "SC call" ) // ArgsScCallExecutor represents the DTO struct for creating a new instance of type scCallExecutor type ArgsScCallExecutor struct { - ScProxyBech32Addresses []string - Proxy Proxy - Codec Codec - Filter ScCallsExecuteFilter - Log logger.Logger - ExtraGasToExecute uint64 - MaxGasLimitToUse uint64 - GasLimitForOutOfGasTransactions uint64 - NonceTxHandler NonceTransactionsHandler - PrivateKey crypto.PrivateKey - SingleSigner crypto.SingleSigner - TransactionChecks config.TransactionChecksConfig - CloseAppChan chan struct{} + TransactionExecutor TransactionExecutor + ScProxyBech32Addresses []string + Proxy Proxy + Codec Codec + Filter ScCallsExecuteFilter + Log logger.Logger + ExecutorConfig config.ScCallsExecutorConfig + TransactionChecks config.TransactionChecksConfig } type scCallExecutor struct { scProxyBech32Addresses []string proxy Proxy + transactionExecutor TransactionExecutor codec Codec filter ScCallsExecuteFilter log logger.Logger extraGasToExecute uint64 maxGasLimitToUse uint64 gasLimitForOutOfGasTransactions uint64 - nonceTxHandler NonceTransactionsHandler - privateKey crypto.PrivateKey - singleSigner crypto.SingleSigner - senderAddress core.AddressHandler - numSentTransactions uint32 - checkTransactionResults bool - timeBetweenChecks time.Duration executionTimeout time.Duration - closeAppOnError bool - extraDelayOnError time.Duration - closeAppChan chan struct{} } // NewScCallExecutor creates a new instance of type scCallExecutor func NewScCallExecutor(args ArgsScCallExecutor) (*scCallExecutor, error) { - err := checkArgs(args) + err := checkScCallExecutorArgs(args) if err != nil { return nil, err } - publicKey := args.PrivateKey.GeneratePublic() - publicKeyBytes, err := publicKey.ToByteArray() - if err != nil { - return nil, err - } - senderAddress := data.NewAddressFromBytes(publicKeyBytes) - return &scCallExecutor{ scProxyBech32Addresses: args.ScProxyBech32Addresses, proxy: args.Proxy, + transactionExecutor: args.TransactionExecutor, codec: args.Codec, filter: args.Filter, log: args.Log, - extraGasToExecute: args.ExtraGasToExecute, - maxGasLimitToUse: args.MaxGasLimitToUse, - gasLimitForOutOfGasTransactions: args.GasLimitForOutOfGasTransactions, - nonceTxHandler: args.NonceTxHandler, - privateKey: args.PrivateKey, - singleSigner: args.SingleSigner, - senderAddress: senderAddress, - checkTransactionResults: args.TransactionChecks.CheckTransactionResults, - timeBetweenChecks: time.Second * time.Duration(args.TransactionChecks.TimeInSecondsBetweenChecks), + extraGasToExecute: args.ExecutorConfig.ExtraGasToExecute, + maxGasLimitToUse: args.ExecutorConfig.MaxGasLimitToUse, + gasLimitForOutOfGasTransactions: args.ExecutorConfig.GasLimitForOutOfGasTransactions, executionTimeout: time.Second * time.Duration(args.TransactionChecks.ExecutionTimeoutInSeconds), - closeAppOnError: args.TransactionChecks.CloseAppOnError, - extraDelayOnError: time.Second * time.Duration(args.TransactionChecks.ExtraDelayInSecondsOnError), - closeAppChan: args.CloseAppChan, }, nil } -func checkArgs(args ArgsScCallExecutor) error { +func checkScCallExecutorArgs(args ArgsScCallExecutor) error { if check.IfNil(args.Proxy) { return errNilProxy } + if check.IfNil(args.TransactionExecutor) { + return errNilTransactionExecutor + } if check.IfNil(args.Codec) { return errNilCodec } @@ -117,24 +86,11 @@ func checkArgs(args ArgsScCallExecutor) error { if check.IfNil(args.Log) { return errNilLogger } - if check.IfNil(args.NonceTxHandler) { - return errNilNonceTxHandler - } - if check.IfNil(args.PrivateKey) { - return errNilPrivateKey + if args.ExecutorConfig.MaxGasLimitToUse < minGasToExecuteSCCalls { + return fmt.Errorf("%w for MaxGasLimitToUse: provided: %d, absolute minimum required: %d", errGasLimitIsLessThanAbsoluteMinimum, args.ExecutorConfig.MaxGasLimitToUse, minGasToExecuteSCCalls) } - if check.IfNil(args.SingleSigner) { - return errNilSingleSigner - } - if args.MaxGasLimitToUse < minGasToExecuteSCCalls { - return fmt.Errorf("%w for MaxGasLimitToUse: provided: %d, absolute minimum required: %d", errGasLimitIsLessThanAbsoluteMinimum, args.MaxGasLimitToUse, minGasToExecuteSCCalls) - } - if args.GasLimitForOutOfGasTransactions < minGasToExecuteSCCalls { - return fmt.Errorf("%w for GasLimitForOutOfGasTransactions: provided: %d, absolute minimum required: %d", errGasLimitIsLessThanAbsoluteMinimum, args.GasLimitForOutOfGasTransactions, minGasToExecuteSCCalls) - } - //TODO: remove this in the next PR - if args.CloseAppChan == nil && args.TransactionChecks.CloseAppOnError { - return fmt.Errorf("%w while the TransactionChecks.CloseAppOnError is set to true", errNilCloseAppChannel) + if args.ExecutorConfig.GasLimitForOutOfGasTransactions < minGasToExecuteSCCalls { + return fmt.Errorf("%w for GasLimitForOutOfGasTransactions: provided: %d, absolute minimum required: %d", errGasLimitIsLessThanAbsoluteMinimum, args.ExecutorConfig.GasLimitForOutOfGasTransactions, minGasToExecuteSCCalls) } err := checkTransactionChecksConfig(args.TransactionChecks, args.Log) if err != nil { @@ -284,34 +240,19 @@ func (executor *scCallExecutor) executeOperation( return err } - bech32Address, err := executor.senderAddress.AddressAsBech32String() - if err != nil { - return err - } - - gasLimit, err := executor.codec.ExtractGasLimitFromRawCallData(callData.RawCallData) + providedGasLimit, err := executor.codec.ExtractGasLimitFromRawCallData(callData.RawCallData) if err != nil { executor.log.Warn("scCallExecutor.executeOperation found a non-parsable raw call data", "raw call data", callData.RawCallData, "error", err) - gasLimit = 0 - } - - tx := &transaction.FrontendTransaction{ - ChainID: networkConfig.ChainID, - Version: networkConfig.MinTransactionVersion, - GasLimit: gasLimit + executor.extraGasToExecute, - Data: dataBytes, - Sender: bech32Address, - Receiver: scProxyAddress, - Value: "0", + providedGasLimit = 0 } + txGasLimit := providedGasLimit + executor.extraGasToExecute to, _ := callData.To.AddressAsBech32String() - if tx.GasLimit > contractMaxGasLimit { + if txGasLimit > contractMaxGasLimit { // the contract will refund this transaction, so we will use less gas to preserve funds executor.log.Warn("setting a lower gas limit for this transaction because it will be refunded", - "computed gas limit", tx.GasLimit, - "max allowed", executor.maxGasLimitToUse, + "computed gas limit", txGasLimit, "data", dataBytes, "from", callData.From.Hex(), "to", to, @@ -319,13 +260,12 @@ func (executor *scCallExecutor) executeOperation( "amount", callData.Amount, "nonce", callData.Nonce, ) - tx.GasLimit = executor.gasLimitForOutOfGasTransactions + txGasLimit = executor.gasLimitForOutOfGasTransactions } - - if tx.GasLimit > executor.maxGasLimitToUse { + if txGasLimit > executor.maxGasLimitToUse { executor.log.Warn("can not execute transaction because the provided gas limit on the SC call exceeds "+ "the maximum gas limit allowance for this executor, WILL SKIP the execution", - "computed gas limit", tx.GasLimit, + "provided gas limit", txGasLimit, "max allowed", executor.maxGasLimitToUse, "data", dataBytes, "from", callData.From.Hex(), @@ -338,152 +278,12 @@ func (executor *scCallExecutor) executeOperation( return nil } - err = executor.nonceTxHandler.ApplyNonceAndGasPrice(ctx, executor.senderAddress, tx) - if err != nil { - return err - } - - err = executor.signTransactionWithPrivateKey(tx) - if err != nil { - return err - } - - hash, err := executor.nonceTxHandler.SendTransaction(ctx, tx) - if err != nil { - return err - } - - executor.log.Info("scCallExecutor.executeOperation: sent transaction from executor", - "hash", hash, - "tx ID", id, - "call data", callData.String(), - "extra gas", executor.extraGasToExecute, - "sender", bech32Address, - "to", to) - - atomic.AddUint32(&executor.numSentTransactions, 1) - - return executor.handleResults(ctx, hash) -} - -func (executor *scCallExecutor) handleResults(ctx context.Context, hash string) error { - if !executor.checkTransactionResults { - return nil - } - - err := executor.checkResultsUntilDone(ctx, hash) - executor.waitForExtraDelay(ctx, err) - return err -} - -// signTransactionWithPrivateKey signs a transaction with the client's private key -func (executor *scCallExecutor) signTransactionWithPrivateKey(tx *transaction.FrontendTransaction) error { - tx.Signature = "" - bytes, err := json.Marshal(&tx) - if err != nil { - return err - } - - signature, err := executor.singleSigner.Sign(executor.privateKey, bytes) - if err != nil { - return err - } - - tx.Signature = hex.EncodeToString(signature) - - return nil -} - -func (executor *scCallExecutor) checkResultsUntilDone(ctx context.Context, hash string) error { - timer := time.NewTimer(executor.timeBetweenChecks) - defer timer.Stop() - - for { - timer.Reset(executor.timeBetweenChecks) - - select { - case <-ctx.Done(): - return ctx.Err() - case <-timer.C: - err, shouldStop := executor.checkResults(ctx, hash) - if shouldStop { - executor.handleError(ctx, err) - return err - } - } - } -} - -func (executor *scCallExecutor) checkResults(ctx context.Context, hash string) (error, bool) { - txStatus, err := executor.proxy.ProcessTransactionStatus(ctx, hash) - if err != nil { - if err.Error() == transactionNotFoundErrString { - return nil, false - } - - return err, true - } - - if txStatus == transaction.TxStatusSuccess { - return nil, true - } - if txStatus == transaction.TxStatusPending { - return nil, false - } - - executor.logFullTransaction(ctx, hash) - return fmt.Errorf("%w for tx hash %s", errTransactionFailed, hash), true -} - -func (executor *scCallExecutor) handleError(ctx context.Context, err error) { - if err == nil { - return - } - if !executor.closeAppOnError { - return - } - - go func() { - // wait here until we could write in the close app chan - // ... or the context expired (application might close) - select { - case <-ctx.Done(): - case executor.closeAppChan <- struct{}{}: - } - }() -} - -func (executor *scCallExecutor) logFullTransaction(ctx context.Context, hash string) { - txData, err := executor.proxy.GetTransactionInfoWithResults(ctx, hash) - if err != nil { - executor.log.Error("error getting the transaction for display", "error", err) - return - } - - txDataString, err := json.MarshalIndent(txData.Data.Transaction, "", " ") - if err != nil { - executor.log.Error("error preparing transaction for display", "error", err) - return - } - - executor.log.Error("transaction failed", "hash", hash, "full transaction details", string(txDataString)) -} - -func (executor *scCallExecutor) waitForExtraDelay(ctx context.Context, err error) { - if err == nil { - return - } - - timer := time.NewTimer(executor.extraDelayOnError) - select { - case <-ctx.Done(): - case <-timer.C: - } + return executor.transactionExecutor.ExecuteTransaction(ctx, networkConfig, scProxyAddress, scCallTxType, txGasLimit, dataBytes) } // GetNumSentTransaction returns the total sent transactions func (executor *scCallExecutor) GetNumSentTransaction() uint32 { - return atomic.LoadUint32(&executor.numSentTransactions) + return executor.transactionExecutor.GetNumSentTransaction() } // IsInterfaceNil returns true if there is no value under the interface diff --git a/executors/multiversx/scCallsExecutor_test.go b/executors/multiversx/scCallsExecutor_test.go index ab7eba68..50782599 100644 --- a/executors/multiversx/scCallsExecutor_test.go +++ b/executors/multiversx/scCallsExecutor_test.go @@ -3,23 +3,16 @@ package multiversx import ( "bytes" "context" - "encoding/hex" "errors" - "fmt" "math/big" - "sync/atomic" "testing" - "time" "github.com/ethereum/go-ethereum/common" + "github.com/multiversx/mx-bridge-eth-go/config" "github.com/multiversx/mx-bridge-eth-go/parsers" "github.com/multiversx/mx-bridge-eth-go/testsCommon" - testCrypto "github.com/multiversx/mx-bridge-eth-go/testsCommon/crypto" "github.com/multiversx/mx-bridge-eth-go/testsCommon/interactors" - "github.com/multiversx/mx-chain-core-go/data/transaction" "github.com/multiversx/mx-chain-core-go/data/vm" - crypto "github.com/multiversx/mx-chain-crypto-go" - "github.com/multiversx/mx-sdk-go/core" "github.com/multiversx/mx-sdk-go/data" "github.com/stretchr/testify/assert" ) @@ -31,17 +24,16 @@ func createMockArgsScCallExecutor() ArgsScCallExecutor { ScProxyBech32Addresses: []string{ "erd1qqqqqqqqqqqqqpgqk839entmk46ykukvhpn90g6knskju3dtanaq20f66e", }, - Proxy: &interactors.ProxyStub{}, - Codec: &testsCommon.MultiversxCodecStub{}, - Filter: &testsCommon.ScCallsExecuteFilterStub{}, - Log: &testsCommon.LoggerStub{}, - ExtraGasToExecute: 100, - MaxGasLimitToUse: minGasToExecuteSCCalls, - GasLimitForOutOfGasTransactions: minGasToExecuteSCCalls, - NonceTxHandler: &testsCommon.TxNonceHandlerV2Stub{}, - PrivateKey: testCrypto.NewPrivateKeyMock(), - SingleSigner: &testCrypto.SingleSignerStub{}, - CloseAppChan: make(chan struct{}), + Proxy: &interactors.ProxyStub{}, + TransactionExecutor: &testsCommon.TransactionExecutorStub{}, + Codec: &testsCommon.MultiversxCodecStub{}, + Filter: &testsCommon.ScCallsExecuteFilterStub{}, + Log: &testsCommon.LoggerStub{}, + ExecutorConfig: config.ScCallsExecutorConfig{ + ExtraGasToExecute: 100, + MaxGasLimitToUse: minGasToExecuteSCCalls, + GasLimitForOutOfGasTransactions: minGasToExecuteSCCalls, + }, } } @@ -77,6 +69,16 @@ func TestNewScCallExecutor(t *testing.T) { assert.Nil(t, executor) assert.Equal(t, errNilProxy, err) }) + t.Run("nil transaction executor should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsScCallExecutor() + args.TransactionExecutor = nil + + executor, err := NewScCallExecutor(args) + assert.Nil(t, executor) + assert.Equal(t, errNilTransactionExecutor, err) + }) t.Run("nil codec should error", func(t *testing.T) { t.Parallel() @@ -107,36 +109,6 @@ func TestNewScCallExecutor(t *testing.T) { assert.Nil(t, executor) assert.Equal(t, errNilLogger, err) }) - t.Run("nil nonce tx handler should error", func(t *testing.T) { - t.Parallel() - - args := createMockArgsScCallExecutor() - args.NonceTxHandler = nil - - executor, err := NewScCallExecutor(args) - assert.Nil(t, executor) - assert.Equal(t, errNilNonceTxHandler, err) - }) - t.Run("nil private key should error", func(t *testing.T) { - t.Parallel() - - args := createMockArgsScCallExecutor() - args.PrivateKey = nil - - executor, err := NewScCallExecutor(args) - assert.Nil(t, executor) - assert.Equal(t, errNilPrivateKey, err) - }) - t.Run("nil single signer should error", func(t *testing.T) { - t.Parallel() - - args := createMockArgsScCallExecutor() - args.SingleSigner = nil - - executor, err := NewScCallExecutor(args) - assert.Nil(t, executor) - assert.Equal(t, errNilSingleSigner, err) - }) t.Run("empty list of sc proxy bech32 addresses should error", func(t *testing.T) { t.Parallel() @@ -181,24 +153,12 @@ func TestNewScCallExecutor(t *testing.T) { assert.ErrorIs(t, err, errInvalidValue) assert.Contains(t, err.Error(), "for TransactionChecks.ExecutionTimeoutInSeconds, minimum: 1, got: 0") }) - t.Run("nil close app chan should error", func(t *testing.T) { - t.Parallel() - - args := createMockArgsScCallExecutor() - args.TransactionChecks = createMockCheckConfigs() - args.CloseAppChan = nil - - executor, err := NewScCallExecutor(args) - assert.Nil(t, executor) - assert.ErrorIs(t, err, errNilCloseAppChannel) - assert.Contains(t, err.Error(), "while the TransactionChecks.CloseAppOnError is set to true") - }) t.Run("invalid MaxGasLimitToUse should error", func(t *testing.T) { t.Parallel() args := createMockArgsScCallExecutor() args.TransactionChecks = createMockCheckConfigs() - args.MaxGasLimitToUse = minGasToExecuteSCCalls - 1 + args.ExecutorConfig.MaxGasLimitToUse = minGasToExecuteSCCalls - 1 executor, err := NewScCallExecutor(args) assert.Nil(t, executor) @@ -211,7 +171,7 @@ func TestNewScCallExecutor(t *testing.T) { args := createMockArgsScCallExecutor() args.TransactionChecks = createMockCheckConfigs() - args.GasLimitForOutOfGasTransactions = minGasToExecuteSCCalls - 1 + args.ExecutorConfig.GasLimitForOutOfGasTransactions = minGasToExecuteSCCalls - 1 executor, err := NewScCallExecutor(args) assert.Nil(t, executor) @@ -257,15 +217,11 @@ func TestScCallExecutor_Execute(t *testing.T) { expectedError := errors.New("expected error") argsForErrors := createMockArgsScCallExecutor() - argsForErrors.NonceTxHandler = &testsCommon.TxNonceHandlerV2Stub{ - ApplyNonceAndGasPriceCalled: func(ctx context.Context, address core.AddressHandler, tx *transaction.FrontendTransaction) error { - assert.Fail(t, "should have not called ApplyNonceAndGasPriceCalled") + argsForErrors.TransactionExecutor = &testsCommon.TransactionExecutorStub{ + ExecuteTransactionCalled: func(ctx context.Context, networkConfig *data.NetworkConfig, receiver string, transactionType string, gasLimit uint64, dataBytes []byte) error { + assert.Fail(t, "should have not called ExecuteTransactionCalled") return runError }, - SendTransactionCalled: func(ctx context.Context, tx *transaction.FrontendTransaction) (string, error) { - assert.Fail(t, "should have not called SendTransactionCalled") - return "", runError - }, } t.Run("get pending errors, should error", func(t *testing.T) { @@ -283,7 +239,6 @@ func TestScCallExecutor_Execute(t *testing.T) { assert.NotNil(t, err) assert.Contains(t, err.Error(), expectedError.Error()) assert.Contains(t, err.Error(), "errors found during execution") - assert.Zero(t, executor.GetNumSentTransaction()) }) t.Run("get pending returns a not ok status, should error", func(t *testing.T) { t.Parallel() @@ -303,7 +258,6 @@ func TestScCallExecutor_Execute(t *testing.T) { err := executor.Execute(context.Background()) assert.NotNil(t, err) assert.Contains(t, err.Error(), "got response code 'NOT OK'") - assert.Zero(t, executor.GetNumSentTransaction()) }) t.Run("get pending returns an odd number of lines, should error", func(t *testing.T) { t.Parallel() @@ -328,7 +282,6 @@ func TestScCallExecutor_Execute(t *testing.T) { assert.Contains(t, err.Error(), errInvalidNumberOfResponseLines.Error()) assert.Contains(t, err.Error(), "errors found during execution") assert.Contains(t, err.Error(), "expected an even number, got 1") - assert.Zero(t, executor.GetNumSentTransaction()) }) t.Run("decoder errors, should error", func(t *testing.T) { t.Parallel() @@ -362,7 +315,6 @@ func TestScCallExecutor_Execute(t *testing.T) { assert.NotNil(t, err) assert.Contains(t, err.Error(), expectedError.Error()) assert.Contains(t, err.Error(), "errors found during execution") - assert.Zero(t, executor.GetNumSentTransaction()) }) t.Run("get network configs errors, should error", func(t *testing.T) { t.Parallel() @@ -399,20 +351,15 @@ func TestScCallExecutor_Execute(t *testing.T) { assert.NotNil(t, err) assert.Contains(t, err.Error(), expectedError.Error()) assert.Contains(t, err.Error(), "errors found during execution") - assert.Zero(t, executor.GetNumSentTransaction()) }) - t.Run("ApplyNonceAndGasPrice errors, should error", func(t *testing.T) { + t.Run("SendTransaction errors, should error", func(t *testing.T) { t.Parallel() args := createMockArgsScCallExecutor() - args.NonceTxHandler = &testsCommon.TxNonceHandlerV2Stub{ - ApplyNonceAndGasPriceCalled: func(ctx context.Context, address core.AddressHandler, tx *transaction.FrontendTransaction) error { + args.TransactionExecutor = &testsCommon.TransactionExecutorStub{ + ExecuteTransactionCalled: func(ctx context.Context, networkConfig *data.NetworkConfig, receiver string, transactionType string, gasLimit uint64, dataBytes []byte) error { return expectedError }, - SendTransactionCalled: func(ctx context.Context, tx *transaction.FrontendTransaction) (string, error) { - assert.Fail(t, "should have not called SendTransactionCalled") - return "", runError - }, } args.Proxy = &interactors.ProxyStub{ ExecuteVMQueryCalled: func(ctx context.Context, vmRequest *data.VmValueRequest) (*data.VmValuesResponseData, error) { @@ -445,71 +392,15 @@ func TestScCallExecutor_Execute(t *testing.T) { assert.NotNil(t, err) assert.Contains(t, err.Error(), expectedError.Error()) assert.Contains(t, err.Error(), "errors found during execution") - assert.Zero(t, executor.GetNumSentTransaction()) }) - t.Run("Sign errors, should error", func(t *testing.T) { + t.Run("should not execute transactions with high gas limit usage", func(t *testing.T) { t.Parallel() args := createMockArgsScCallExecutor() - args.NonceTxHandler = &testsCommon.TxNonceHandlerV2Stub{ - ApplyNonceAndGasPriceCalled: func(ctx context.Context, address core.AddressHandler, tx *transaction.FrontendTransaction) error { - return nil - }, - SendTransactionCalled: func(ctx context.Context, tx *transaction.FrontendTransaction) (string, error) { - assert.Fail(t, "should have not called SendTransactionCalled") - return "", runError - }, - } - args.Proxy = &interactors.ProxyStub{ - ExecuteVMQueryCalled: func(ctx context.Context, vmRequest *data.VmValueRequest) (*data.VmValuesResponseData, error) { - return &data.VmValuesResponseData{ - Data: &vm.VMOutputApi{ - ReturnCode: okCodeAfterExecution, - ReturnData: [][]byte{ - {0x01}, - {0x03, 0x04}, - }, - }, - }, nil - }, - GetNetworkConfigCalled: func(ctx context.Context) (*data.NetworkConfig, error) { - return &data.NetworkConfig{}, nil - }, - } - args.Codec = &testsCommon.MultiversxCodecStub{ - DecodeProxySCCompleteCallDataCalled: func(buff []byte) (parsers.ProxySCCompleteCallData, error) { - assert.Equal(t, []byte{0x03, 0x04}, buff) - - return parsers.ProxySCCompleteCallData{ - To: data.NewAddressFromBytes(bytes.Repeat([]byte{1}, 32)), - }, nil - }, - } - args.SingleSigner = &testCrypto.SingleSignerStub{ - SignCalled: func(private crypto.PrivateKey, msg []byte) ([]byte, error) { - return nil, expectedError - }, - } - - executor, _ := NewScCallExecutor(args) - err := executor.Execute(context.Background()) - assert.NotNil(t, err) - assert.Contains(t, err.Error(), expectedError.Error()) - assert.Contains(t, err.Error(), "errors found during execution") - assert.Zero(t, executor.GetNumSentTransaction()) - }) - t.Run("SendTransaction errors, should error", func(t *testing.T) { - t.Parallel() + args.ExecutorConfig.MaxGasLimitToUse = 5000000 + args.TransactionChecks = createMockCheckConfigs() + args.TransactionChecks.TimeInSecondsBetweenChecks = 1 - args := createMockArgsScCallExecutor() - args.NonceTxHandler = &testsCommon.TxNonceHandlerV2Stub{ - ApplyNonceAndGasPriceCalled: func(ctx context.Context, address core.AddressHandler, tx *transaction.FrontendTransaction) error { - return nil - }, - SendTransactionCalled: func(ctx context.Context, tx *transaction.FrontendTransaction) (string, error) { - return "", expectedError - }, - } args.Proxy = &interactors.ProxyStub{ ExecuteVMQueryCalled: func(ctx context.Context, vmRequest *data.VmValueRequest) (*data.VmValuesResponseData, error) { return &data.VmValuesResponseData{ @@ -537,28 +428,28 @@ func TestScCallExecutor_Execute(t *testing.T) { }, ExtractGasLimitFromRawCallDataCalled: func(buff []byte) (uint64, error) { assert.Equal(t, "dummy", string(buff)) - return 1000000, nil + return 5000000, nil + }, + } + args.TransactionExecutor = &testsCommon.TransactionExecutorStub{ + ExecuteTransactionCalled: func(ctx context.Context, networkConfig *data.NetworkConfig, receiver string, transactionType string, gasLimit uint64, dataBytes []byte) error { + assert.Fail(t, "should have not called execute transaction") + + return nil }, } executor, _ := NewScCallExecutor(args) err := executor.Execute(context.Background()) - assert.NotNil(t, err) - assert.Contains(t, err.Error(), expectedError.Error()) - assert.Contains(t, err.Error(), "errors found during execution") - assert.Equal(t, uint32(0), executor.GetNumSentTransaction()) + assert.Nil(t, err) }) - t.Run("should work with one SC proxy address", func(t *testing.T) { + t.Run("should work", func(t *testing.T) { t.Parallel() args := createMockArgsScCallExecutor() - args.MaxGasLimitToUse = 250000000 + args.ExecutorConfig.MaxGasLimitToUse = 250000000 args.TransactionChecks = createMockCheckConfigs() args.TransactionChecks.TimeInSecondsBetweenChecks = 1 - txHash := "tx hash" - processTransactionStatusCalled := false - - nonceCounter := uint64(100) sendWasCalled := false args.Proxy = &interactors.ProxyStub{ @@ -584,12 +475,6 @@ func TestScCallExecutor_Execute(t *testing.T) { MinTransactionVersion: 111, }, nil }, - ProcessTransactionStatusCalled: func(ctx context.Context, hexTxHash string) (transaction.TxStatus, error) { - assert.Equal(t, txHash, hexTxHash) - processTransactionStatusCalled = true - - return transaction.TxStatusSuccess, nil - }, } args.Codec = &testsCommon.MultiversxCodecStub{ DecodeProxySCCompleteCallDataCalled: func(buff []byte) (parsers.ProxySCCompleteCallData, error) { @@ -613,37 +498,20 @@ func TestScCallExecutor_Execute(t *testing.T) { return callData.Token == "tkn2" }, } - args.NonceTxHandler = &testsCommon.TxNonceHandlerV2Stub{ - ApplyNonceAndGasPriceCalled: func(ctx context.Context, address core.AddressHandler, tx *transaction.FrontendTransaction) error { - tx.Nonce = nonceCounter - tx.GasPrice = 101010 - nonceCounter++ - return nil - }, - SendTransactionCalled: func(ctx context.Context, tx *transaction.FrontendTransaction) (string, error) { - assert.Equal(t, "TEST", tx.ChainID) - assert.Equal(t, uint32(111), tx.Version) - assert.Equal(t, args.ExtraGasToExecute+5000000, tx.GasLimit) - assert.Equal(t, nonceCounter-1, tx.Nonce) - assert.Equal(t, uint64(101010), tx.GasPrice) - assert.Equal(t, hex.EncodeToString([]byte("sig")), tx.Signature) - _, err := data.NewAddressFromBech32String(tx.Sender) - assert.Nil(t, err) - assert.Equal(t, "erd1qqqqqqqqqqqqqpgqk839entmk46ykukvhpn90g6knskju3dtanaq20f66e", tx.Receiver) - assert.Equal(t, "0", tx.Value) - - // only the second pending operation got through the filter + args.TransactionExecutor = &testsCommon.TransactionExecutorStub{ + ExecuteTransactionCalled: func(ctx context.Context, networkConfig *data.NetworkConfig, receiver string, transactionType string, gasLimit uint64, dataBytes []byte) error { + assert.Equal(t, "TEST", networkConfig.ChainID) + assert.Equal(t, uint32(111), networkConfig.MinTransactionVersion) + assert.Equal(t, args.ExecutorConfig.ExtraGasToExecute+5000000, gasLimit) + assert.Equal(t, "erd1qqqqqqqqqqqqqpgqk839entmk46ykukvhpn90g6knskju3dtanaq20f66e", receiver) + assert.Equal(t, scCallTxType, transactionType) + expectedData := scProxyCallFunction + "@02" - assert.Equal(t, expectedData, string(tx.Data)) + assert.Equal(t, expectedData, string(dataBytes)) sendWasCalled = true - return txHash, nil - }, - } - args.SingleSigner = &testCrypto.SingleSignerStub{ - SignCalled: func(private crypto.PrivateKey, msg []byte) ([]byte, error) { - return []byte("sig"), nil + return nil }, } @@ -652,28 +520,16 @@ func TestScCallExecutor_Execute(t *testing.T) { err := executor.Execute(context.Background()) assert.Nil(t, err) assert.True(t, sendWasCalled) - assert.Equal(t, uint32(1), executor.GetNumSentTransaction()) - assert.True(t, processTransactionStatusCalled) }) t.Run("should work with one two proxy address", func(t *testing.T) { t.Parallel() args := createMockArgsScCallExecutor() - pk := args.PrivateKey.GeneratePublic() - pkBuff, _ := pk.ToByteArray() - sender := data.NewAddressFromBytes(pkBuff) - senderAddress, _ := sender.AddressAsBech32String() - args.ScProxyBech32Addresses = append(args.ScProxyBech32Addresses, "erd1qqqqqqqqqqqqqpgqzyuaqg3dl7rqlkudrsnm5ek0j3a97qevd8sszj0glf") - args.MaxGasLimitToUse = 250000000 + args.ExecutorConfig.MaxGasLimitToUse = 250000000 args.TransactionChecks = createMockCheckConfigs() args.TransactionChecks.TimeInSecondsBetweenChecks = 1 - txHash := "tx hash" - numProcessTransactionStatusCalled := 0 - - nonceCounter := uint64(100) - sentTransactions := make([]*transaction.FrontendTransaction, 0) args.Proxy = &interactors.ProxyStub{ ExecuteVMQueryCalled: func(ctx context.Context, vmRequest *data.VmValueRequest) (*data.VmValuesResponseData, error) { assert.Equal(t, getPendingTransactionsFunction, vmRequest.FuncName) @@ -704,12 +560,6 @@ func TestScCallExecutor_Execute(t *testing.T) { MinTransactionVersion: 111, }, nil }, - ProcessTransactionStatusCalled: func(ctx context.Context, hexTxHash string) (transaction.TxStatus, error) { - assert.Contains(t, hexTxHash, txHash) - numProcessTransactionStatusCalled++ - - return transaction.TxStatusSuccess, nil - }, } args.Codec = &testsCommon.MultiversxCodecStub{ DecodeProxySCCompleteCallDataCalled: func(buff []byte) (parsers.ProxySCCompleteCallData, error) { @@ -739,49 +589,40 @@ func TestScCallExecutor_Execute(t *testing.T) { return callData.Token == "tkn2" || callData.Token == "tkn4" }, } - args.NonceTxHandler = &testsCommon.TxNonceHandlerV2Stub{ - ApplyNonceAndGasPriceCalled: func(ctx context.Context, address core.AddressHandler, tx *transaction.FrontendTransaction) error { - tx.Nonce = nonceCounter - tx.GasPrice = 101010 - nonceCounter++ - return nil - }, - SendTransactionCalled: func(ctx context.Context, tx *transaction.FrontendTransaction) (string, error) { - sentTransactions = append(sentTransactions, tx) - return fmt.Sprintf("%s - %d", txHash, tx.Nonce), nil - }, + type sentTxValues struct { + receiver string + transactionType string + gasLimit uint64 + dataBytes []byte } - args.SingleSigner = &testCrypto.SingleSignerStub{ - SignCalled: func(private crypto.PrivateKey, msg []byte) ([]byte, error) { - return []byte("sig"), nil + sentTransactions := make([]*sentTxValues, 0) + args.TransactionExecutor = &testsCommon.TransactionExecutorStub{ + ExecuteTransactionCalled: func(ctx context.Context, networkConfig *data.NetworkConfig, receiver string, transactionType string, gasLimit uint64, dataBytes []byte) error { + tx := &sentTxValues{ + receiver: receiver, + transactionType: transactionType, + gasLimit: gasLimit, + dataBytes: dataBytes, + } + sentTransactions = append(sentTransactions, tx) + + return nil }, } - expectedSentTransactions := []*transaction.FrontendTransaction{ + expectedSentTransactions := []*sentTxValues{ { - Nonce: 100, - Value: "0", - Receiver: "erd1qqqqqqqqqqqqqpgqk839entmk46ykukvhpn90g6knskju3dtanaq20f66e", - Sender: senderAddress, - GasPrice: 101010, - GasLimit: args.ExtraGasToExecute + 5000000, - Data: []byte(scProxyCallFunction + "@02"), - Signature: hex.EncodeToString([]byte("sig")), - ChainID: "TEST", - Version: 111, + receiver: "erd1qqqqqqqqqqqqqpgqk839entmk46ykukvhpn90g6knskju3dtanaq20f66e", + transactionType: scCallTxType, + gasLimit: args.ExecutorConfig.ExtraGasToExecute + 5000000, + dataBytes: []byte(scProxyCallFunction + "@02"), }, { - Nonce: 101, - Value: "0", - Receiver: "erd1qqqqqqqqqqqqqpgqzyuaqg3dl7rqlkudrsnm5ek0j3a97qevd8sszj0glf", - Sender: senderAddress, - GasPrice: 101010, - GasLimit: args.ExtraGasToExecute + 5000000, - Data: []byte(scProxyCallFunction + "@04"), - Signature: hex.EncodeToString([]byte("sig")), - ChainID: "TEST", - Version: 111, + receiver: "erd1qqqqqqqqqqqqqpgqzyuaqg3dl7rqlkudrsnm5ek0j3a97qevd8sszj0glf", + transactionType: scCallTxType, + gasLimit: args.ExecutorConfig.ExtraGasToExecute + 5000000, + dataBytes: []byte(scProxyCallFunction + "@04"), }, } @@ -789,16 +630,12 @@ func TestScCallExecutor_Execute(t *testing.T) { err := executor.Execute(context.Background()) assert.Nil(t, err) - assert.Equal(t, uint32(2), executor.GetNumSentTransaction()) assert.Equal(t, expectedSentTransactions, sentTransactions) - assert.Equal(t, 2, numProcessTransactionStatusCalled) }) t.Run("should work even if the gas limit decode errors", func(t *testing.T) { t.Parallel() args := createMockArgsScCallExecutor() - - nonceCounter := uint64(100) sendWasCalled := false args.Proxy = &interactors.ProxyStub{ @@ -845,37 +682,20 @@ func TestScCallExecutor_Execute(t *testing.T) { return callData.Token == "tkn2" }, } - args.NonceTxHandler = &testsCommon.TxNonceHandlerV2Stub{ - ApplyNonceAndGasPriceCalled: func(ctx context.Context, address core.AddressHandler, tx *transaction.FrontendTransaction) error { - tx.Nonce = nonceCounter - tx.GasPrice = 101010 - nonceCounter++ - return nil - }, - SendTransactionCalled: func(ctx context.Context, tx *transaction.FrontendTransaction) (string, error) { - assert.Equal(t, "TEST", tx.ChainID) - assert.Equal(t, uint32(111), tx.Version) - assert.Equal(t, args.ExtraGasToExecute, tx.GasLimit) // no 5000000 added gas limit because it wasn't extracted - assert.Equal(t, nonceCounter-1, tx.Nonce) - assert.Equal(t, uint64(101010), tx.GasPrice) - assert.Equal(t, hex.EncodeToString([]byte("sig")), tx.Signature) - _, err := data.NewAddressFromBech32String(tx.Sender) - assert.Nil(t, err) - assert.Equal(t, "erd1qqqqqqqqqqqqqpgqk839entmk46ykukvhpn90g6knskju3dtanaq20f66e", tx.Receiver) - assert.Equal(t, "0", tx.Value) - - // only the second pending operation got through the filter + args.TransactionExecutor = &testsCommon.TransactionExecutorStub{ + ExecuteTransactionCalled: func(ctx context.Context, networkConfig *data.NetworkConfig, receiver string, transactionType string, gasLimit uint64, dataBytes []byte) error { + assert.Equal(t, "TEST", networkConfig.ChainID) + assert.Equal(t, uint32(111), networkConfig.MinTransactionVersion) + assert.Equal(t, args.ExecutorConfig.ExtraGasToExecute, gasLimit) + assert.Equal(t, "erd1qqqqqqqqqqqqqpgqk839entmk46ykukvhpn90g6knskju3dtanaq20f66e", receiver) + assert.Equal(t, scCallTxType, transactionType) + expectedData := scProxyCallFunction + "@02" - assert.Equal(t, expectedData, string(tx.Data)) + assert.Equal(t, expectedData, string(dataBytes)) sendWasCalled = true - return "", nil - }, - } - args.SingleSigner = &testCrypto.SingleSignerStub{ - SignCalled: func(private crypto.PrivateKey, msg []byte) ([]byte, error) { - return []byte("sig"), nil + return nil }, } @@ -884,14 +704,11 @@ func TestScCallExecutor_Execute(t *testing.T) { err := executor.Execute(context.Background()) assert.Nil(t, err) assert.True(t, sendWasCalled) - assert.Equal(t, uint32(1), executor.GetNumSentTransaction()) }) t.Run("should work if the gas limit is above the contract threshold", func(t *testing.T) { t.Parallel() args := createMockArgsScCallExecutor() - - nonceCounter := uint64(100) sendWasCalled := false args.Proxy = &interactors.ProxyStub{ @@ -938,346 +755,41 @@ func TestScCallExecutor_Execute(t *testing.T) { return callData.Token == "tkn2" }, } - args.NonceTxHandler = &testsCommon.TxNonceHandlerV2Stub{ - ApplyNonceAndGasPriceCalled: func(ctx context.Context, address core.AddressHandler, tx *transaction.FrontendTransaction) error { - tx.Nonce = nonceCounter - tx.GasPrice = 101010 - nonceCounter++ - return nil - }, - SendTransactionCalled: func(ctx context.Context, tx *transaction.FrontendTransaction) (string, error) { - assert.Equal(t, "TEST", tx.ChainID) - assert.Equal(t, uint32(111), tx.Version) - assert.Equal(t, args.GasLimitForOutOfGasTransactions, tx.GasLimit) // the gas limit was replaced - assert.Equal(t, nonceCounter-1, tx.Nonce) - assert.Equal(t, uint64(101010), tx.GasPrice) - assert.Equal(t, hex.EncodeToString([]byte("sig")), tx.Signature) - _, err := data.NewAddressFromBech32String(tx.Sender) - assert.Nil(t, err) - assert.Equal(t, "erd1qqqqqqqqqqqqqpgqk839entmk46ykukvhpn90g6knskju3dtanaq20f66e", tx.Receiver) - assert.Equal(t, "0", tx.Value) - - // only the second pending operation got through the filter + args.TransactionExecutor = &testsCommon.TransactionExecutorStub{ + ExecuteTransactionCalled: func(ctx context.Context, networkConfig *data.NetworkConfig, receiver string, transactionType string, gasLimit uint64, dataBytes []byte) error { + assert.Equal(t, "TEST", networkConfig.ChainID) + assert.Equal(t, uint32(111), networkConfig.MinTransactionVersion) + assert.Equal(t, args.ExecutorConfig.GasLimitForOutOfGasTransactions, gasLimit) + assert.Equal(t, "erd1qqqqqqqqqqqqqpgqk839entmk46ykukvhpn90g6knskju3dtanaq20f66e", receiver) + assert.Equal(t, scCallTxType, transactionType) + expectedData := scProxyCallFunction + "@02" - assert.Equal(t, expectedData, string(tx.Data)) + assert.Equal(t, expectedData, string(dataBytes)) sendWasCalled = true - return "", nil - }, - } - args.SingleSigner = &testCrypto.SingleSignerStub{ - SignCalled: func(private crypto.PrivateKey, msg []byte) ([]byte, error) { - return []byte("sig"), nil - }, - } - - executor, _ := NewScCallExecutor(args) - - err := executor.Execute(context.Background()) - assert.Nil(t, err) - assert.True(t, sendWasCalled) - assert.Equal(t, uint32(1), executor.GetNumSentTransaction()) - }) - t.Run("should skip execution if the gas limit exceeds the maximum allowed", func(t *testing.T) { - t.Parallel() - - args := createMockArgsScCallExecutor() - - args.Proxy = &interactors.ProxyStub{ - ExecuteVMQueryCalled: func(ctx context.Context, vmRequest *data.VmValueRequest) (*data.VmValuesResponseData, error) { - assert.Equal(t, args.ScProxyBech32Addresses[0], vmRequest.Address) - assert.Equal(t, getPendingTransactionsFunction, vmRequest.FuncName) - - return &data.VmValuesResponseData{ - Data: &vm.VMOutputApi{ - ReturnCode: okCodeAfterExecution, - ReturnData: [][]byte{ - {0x01}, - []byte("ProxySCCompleteCallData 1"), - {0x02}, - []byte("ProxySCCompleteCallData 2"), - }, - }, - }, nil - }, - GetNetworkConfigCalled: func(ctx context.Context) (*data.NetworkConfig, error) { - return &data.NetworkConfig{ - ChainID: "TEST", - MinTransactionVersion: 111, - }, nil - }, - } - args.Codec = &testsCommon.MultiversxCodecStub{ - DecodeProxySCCompleteCallDataCalled: func(buff []byte) (parsers.ProxySCCompleteCallData, error) { - if string(buff) == "ProxySCCompleteCallData 1" { - return createTestProxySCCompleteCallData("tkn1"), nil - } - if string(buff) == "ProxySCCompleteCallData 2" { - return createTestProxySCCompleteCallData("tkn2"), nil - } - - return parsers.ProxySCCompleteCallData{}, errors.New("wrong buffer") - }, - ExtractGasLimitFromRawCallDataCalled: func(buff []byte) (uint64, error) { - return args.MaxGasLimitToUse - args.ExtraGasToExecute + 1, nil - }, - } - args.Filter = &testsCommon.ScCallsExecuteFilterStub{ - ShouldExecuteCalled: func(callData parsers.ProxySCCompleteCallData) bool { - return callData.Token == "tkn2" - }, - } - args.NonceTxHandler = &testsCommon.TxNonceHandlerV2Stub{ - ApplyNonceAndGasPriceCalled: func(ctx context.Context, address core.AddressHandler, tx *transaction.FrontendTransaction) error { - assert.Fail(t, "should have not apply nonce") return nil }, - SendTransactionCalled: func(ctx context.Context, tx *transaction.FrontendTransaction) (string, error) { - assert.Fail(t, "should have not called send") - - return "", nil - }, - } - args.SingleSigner = &testCrypto.SingleSignerStub{ - SignCalled: func(private crypto.PrivateKey, msg []byte) ([]byte, error) { - return []byte("sig"), nil - }, } executor, _ := NewScCallExecutor(args) err := executor.Execute(context.Background()) assert.Nil(t, err) - assert.Equal(t, uint32(0), executor.GetNumSentTransaction()) + assert.True(t, sendWasCalled) }) } -func TestScCallExecutor_handleResults(t *testing.T) { +func TestScCallExecutor_GetNumSentTransaction(t *testing.T) { t.Parallel() - testHash := "test hash" - t.Run("checkTransactionResults false should not check and return nil", func(t *testing.T) { - t.Parallel() - - args := createMockArgsScCallExecutor() - args.Proxy = &interactors.ProxyStub{ - ProcessTransactionStatusCalled: func(ctx context.Context, hexTxHash string) (transaction.TxStatus, error) { - assert.Fail(t, "should have not called ProcessTransactionStatusCalled") - - return transaction.TxStatusFail, nil - }, - } - - executor, _ := NewScCallExecutor(args) - - err := executor.handleResults(context.Background(), testHash) - assert.Nil(t, err) - }) - t.Run("timeout before process transaction called", func(t *testing.T) { - t.Parallel() - - args := createMockArgsScCallExecutor() - args.Proxy = &interactors.ProxyStub{ - ProcessTransactionStatusCalled: func(ctx context.Context, hexTxHash string) (transaction.TxStatus, error) { - assert.Fail(t, "should have not called ProcessTransactionStatusCalled") - - return transaction.TxStatusFail, nil - }, - } - args.TransactionChecks = createMockCheckConfigs() - - executor, _ := NewScCallExecutor(args) - - workingCtx, cancel := context.WithTimeout(context.Background(), time.Second) - defer cancel() - - err := executor.handleResults(workingCtx, testHash) - assert.ErrorIs(t, err, context.DeadlineExceeded) - }) - t.Run("transaction not found should continuously request the status", func(t *testing.T) { - t.Parallel() - - numRequests := uint64(0) - args := createMockArgsScCallExecutor() - chDone := make(chan struct{}, 1) - args.Proxy = &interactors.ProxyStub{ - ProcessTransactionStatusCalled: func(ctx context.Context, hexTxHash string) (transaction.TxStatus, error) { - atomic.AddUint64(&numRequests, 1) - if atomic.LoadUint64(&numRequests) > 3 { - chDone <- struct{}{} - } - - return transaction.TxStatusInvalid, errors.New("transaction not found") - }, - } - args.TransactionChecks = createMockCheckConfigs() - args.TransactionChecks.TimeInSecondsBetweenChecks = 1 - - executor, _ := NewScCallExecutor(args) - - go func() { - err := executor.handleResults(context.Background(), testHash) - assert.ErrorIs(t, err, context.DeadlineExceeded) // this will be the actual error when the function finishes - }() - - select { - case <-chDone: - return - case <-time.After(time.Second * 30): - assert.Fail(t, "timeout") - } - }) - t.Run("transaction is still pending should continuously request the status", func(t *testing.T) { - t.Parallel() - - numRequests := uint64(0) - args := createMockArgsScCallExecutor() - chDone := make(chan struct{}, 1) - args.Proxy = &interactors.ProxyStub{ - ProcessTransactionStatusCalled: func(ctx context.Context, hexTxHash string) (transaction.TxStatus, error) { - atomic.AddUint64(&numRequests, 1) - if atomic.LoadUint64(&numRequests) > 3 { - chDone <- struct{}{} - } - - return transaction.TxStatusPending, nil - }, - } - args.TransactionChecks = createMockCheckConfigs() - args.TransactionChecks.TimeInSecondsBetweenChecks = 1 - - executor, _ := NewScCallExecutor(args) - - go func() { - err := executor.handleResults(context.Background(), testHash) - assert.ErrorIs(t, err, context.DeadlineExceeded) // this will be the actual error when the function finishes - }() - - select { - case <-chDone: - return - case <-time.After(time.Second * 30): - assert.Fail(t, "timeout") - } - }) - t.Run("error while requesting the status should return the error and wait", func(t *testing.T) { - t.Parallel() - - expectedErr := errors.New("expected error") - args := createMockArgsScCallExecutor() - args.CloseAppChan = make(chan struct{}, 1) - args.Proxy = &interactors.ProxyStub{ - ProcessTransactionStatusCalled: func(ctx context.Context, hexTxHash string) (transaction.TxStatus, error) { - return transaction.TxStatusInvalid, expectedErr - }, - } - args.TransactionChecks = createMockCheckConfigs() - args.TransactionChecks.TimeInSecondsBetweenChecks = 1 - args.TransactionChecks.ExtraDelayInSecondsOnError = 6 - - executor, _ := NewScCallExecutor(args) - - start := time.Now() - err := executor.handleResults(context.Background(), testHash) - assert.Equal(t, expectedErr, err) - end := time.Now() - - assert.GreaterOrEqual(t, end.Sub(start), time.Second*6) - select { - case <-args.CloseAppChan: - default: - assert.Fail(t, "failed to write on the close app chan") - } - }) - t.Run("error while requesting the status should not write on the close app chan, if not enabled", func(t *testing.T) { - t.Parallel() - - expectedErr := errors.New("expected error") - args := createMockArgsScCallExecutor() - args.CloseAppChan = make(chan struct{}, 1) - args.Proxy = &interactors.ProxyStub{ - ProcessTransactionStatusCalled: func(ctx context.Context, hexTxHash string) (transaction.TxStatus, error) { - return transaction.TxStatusInvalid, expectedErr - }, - } - args.TransactionChecks = createMockCheckConfigs() - args.TransactionChecks.TimeInSecondsBetweenChecks = 1 - args.TransactionChecks.ExtraDelayInSecondsOnError = 1 - args.TransactionChecks.CloseAppOnError = false - - executor, _ := NewScCallExecutor(args) - - err := executor.handleResults(context.Background(), testHash) - assert.Equal(t, expectedErr, err) - - select { - case <-args.CloseAppChan: - assert.Fail(t, "should have not written on the close chan") - default: - } - }) - t.Run("transaction failed, should get more info and signal error", func(t *testing.T) { - t.Parallel() - - args := createMockArgsScCallExecutor() - args.CloseAppChan = make(chan struct{}, 1) - args.Proxy = &interactors.ProxyStub{ - ProcessTransactionStatusCalled: func(ctx context.Context, hexTxHash string) (transaction.TxStatus, error) { - return transaction.TxStatusFail, nil - }, - GetTransactionInfoWithResultsCalled: func(ctx context.Context, txHash string) (*data.TransactionInfo, error) { - return &data.TransactionInfo{}, nil - }, - } - args.TransactionChecks = createMockCheckConfigs() - args.TransactionChecks.TimeInSecondsBetweenChecks = 1 - args.TransactionChecks.ExtraDelayInSecondsOnError = 1 - - executor, _ := NewScCallExecutor(args) - - err := executor.handleResults(context.Background(), testHash) - assert.ErrorIs(t, err, errTransactionFailed) - - select { - case <-args.CloseAppChan: - default: - assert.Fail(t, "failed to write on the close app chan") - } - }) - t.Run("transaction failed, get more info fails, should signal error and not panic", func(t *testing.T) { - t.Parallel() - - defer func() { - r := recover() - if r != nil { - assert.Fail(t, fmt.Sprintf("should have not panicked %v", r)) - } - }() - - args := createMockArgsScCallExecutor() - args.CloseAppChan = make(chan struct{}, 1) - args.Proxy = &interactors.ProxyStub{ - ProcessTransactionStatusCalled: func(ctx context.Context, hexTxHash string) (transaction.TxStatus, error) { - return transaction.TxStatusFail, nil - }, - GetTransactionInfoWithResultsCalled: func(ctx context.Context, txHash string) (*data.TransactionInfo, error) { - return nil, fmt.Errorf("random error") - }, - } - args.TransactionChecks = createMockCheckConfigs() - args.TransactionChecks.TimeInSecondsBetweenChecks = 1 - args.TransactionChecks.ExtraDelayInSecondsOnError = 1 - - executor, _ := NewScCallExecutor(args) - - err := executor.handleResults(context.Background(), testHash) - assert.ErrorIs(t, err, errTransactionFailed) + args := createMockArgsScCallExecutor() + args.TransactionExecutor = &testsCommon.TransactionExecutorStub{ + GetNumSentTransactionCalled: func() uint32 { + return 37 + }, + } - select { - case <-args.CloseAppChan: - default: - assert.Fail(t, "failed to write on the close app chan") - } - }) + executor, _ := NewScCallExecutor(args) + assert.Equal(t, uint32(37), executor.GetNumSentTransaction()) } diff --git a/integrationTests/relayers/slowTests/framework/testSetup.go b/integrationTests/relayers/slowTests/framework/testSetup.go index 7fb9e1c9..ff951ede 100644 --- a/integrationTests/relayers/slowTests/framework/testSetup.go +++ b/integrationTests/relayers/slowTests/framework/testSetup.go @@ -116,20 +116,24 @@ func (setup *TestSetup) StartRelayersAndScModule() { func (setup *TestSetup) startScCallerModule() { cfg := config.ScCallsModuleConfig{ - ScProxyBech32Addresses: []string{ - setup.MultiversxHandler.ScProxyAddress.Bech32(), + General: config.GeneralScCallsModuleConfig{ + ScProxyBech32Addresses: []string{ + setup.MultiversxHandler.ScProxyAddress.Bech32(), + }, + NetworkAddress: setup.ChainSimulator.GetNetworkAddress(), + ProxyMaxNoncesDelta: 7, + ProxyFinalityCheck: true, + ProxyCacherExpirationSeconds: 60, + ProxyRestAPIEntityType: string(sdkCore.Proxy), + IntervalToResendTxsInSeconds: 1, + PrivateKeyFile: path.Join(setup.WorkingDir, SCCallerFilename), + }, + ScCallsExecutor: config.ScCallsExecutorConfig{ + ExtraGasToExecute: 60_000_000, // 60 million: this ensures that a SC call with 0 gas limit is refunded + MaxGasLimitToUse: 249_999_999, // max cross shard limit + GasLimitForOutOfGasTransactions: 30_000_000, // gas to use when a higher than max allowed is encountered + PollingIntervalInMillis: 1000, // 1 second }, - ExtraGasToExecute: 60_000_000, // 60 million: this ensures that a SC call with 0 gas limit is refunded - MaxGasLimitToUse: 249_999_999, // max cross shard limit - GasLimitForOutOfGasTransactions: 30_000_000, // gas to use when a higher than max allowed is encountered - NetworkAddress: setup.ChainSimulator.GetNetworkAddress(), - ProxyMaxNoncesDelta: 5, - ProxyFinalityCheck: false, - ProxyCacherExpirationSeconds: 60, // 1 minute - ProxyRestAPIEntityType: string(sdkCore.Proxy), - IntervalToResendTxsInSeconds: 1, - PrivateKeyFile: path.Join(setup.WorkingDir, SCCallerFilename), - PollingIntervalInMillis: 1000, // 1 second Filter: config.PendingOperationsFilterConfig{ AllowedEthAddresses: []string{"*"}, AllowedMvxAddresses: []string{"*"}, diff --git a/testsCommon/transactionExecutorStub.go b/testsCommon/transactionExecutorStub.go new file mode 100644 index 00000000..86f74b88 --- /dev/null +++ b/testsCommon/transactionExecutorStub.go @@ -0,0 +1,43 @@ +package testsCommon + +import ( + "context" + + "github.com/multiversx/mx-sdk-go/data" +) + +// TransactionExecutorStub - +type TransactionExecutorStub struct { + ExecuteTransactionCalled func(ctx context.Context, networkConfig *data.NetworkConfig, receiver string, transactionType string, gasLimit uint64, dataBytes []byte) error + GetNumSentTransactionCalled func() uint32 +} + +// ExecuteTransaction - +func (stub *TransactionExecutorStub) ExecuteTransaction( + ctx context.Context, + networkConfig *data.NetworkConfig, + receiver string, + transactionType string, + gasLimit uint64, + dataBytes []byte, +) error { + if stub.ExecuteTransactionCalled != nil { + return stub.ExecuteTransactionCalled(ctx, networkConfig, receiver, transactionType, gasLimit, dataBytes) + } + + return nil +} + +// GetNumSentTransaction - +func (stub *TransactionExecutorStub) GetNumSentTransaction() uint32 { + if stub.GetNumSentTransactionCalled != nil { + return stub.GetNumSentTransactionCalled() + } + + return 0 +} + +// IsInterfaceNil - +func (stub *TransactionExecutorStub) IsInterfaceNil() bool { + return stub == nil +}