From f6967af45b17872f202fcf79c88831a1efbfd838 Mon Sep 17 00:00:00 2001 From: Iulian Pascalau Date: Thu, 17 Oct 2024 21:20:25 +0300 Subject: [PATCH] - fix executor to prevent the case it can not execute transaction due to higher than allowed gas limit provided --- cmd/scCallsExecutor/config/config.toml | 3 +- cmd/scCallsExecutor/main.go | 29 ++-- config/config.go | 29 ++-- config/tomlConfigs_test.go | 24 +-- executors/multiversx/errors.go | 24 +-- executors/multiversx/module/scCallsModule.go | 25 +-- .../multiversx/module/scCallsModule_test.go | 27 +-- executors/multiversx/scCallsExecutor.go | 123 ++++++++------ executors/multiversx/scCallsExecutor_test.go | 154 ++++++++++++++++-- .../relayers/slowTests/framework/testSetup.go | 23 +-- 10 files changed, 308 insertions(+), 153 deletions(-) diff --git a/cmd/scCallsExecutor/config/config.toml b/cmd/scCallsExecutor/config/config.toml index 57956bc7..b9a18034 100644 --- a/cmd/scCallsExecutor/config/config.toml +++ b/cmd/scCallsExecutor/config/config.toml @@ -1,6 +1,7 @@ ScProxyBech32Address = "erd1qqqqqqqqqqqqqpgqnef5f5aq32d63kljld8w5vnvz4gk5sy9hrrq2ld08s" -ExtraGasToExecute = 60000000 #this value allow the SC calls without provided gas limit to be refunded +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 diff --git a/cmd/scCallsExecutor/main.go b/cmd/scCallsExecutor/main.go index 01ce3842..d1493199 100644 --- a/cmd/scCallsExecutor/main.go +++ b/cmd/scCallsExecutor/main.go @@ -111,20 +111,21 @@ func startExecutor(ctx *cli.Context, version string) error { } args := config.ScCallsModuleConfig{ - ScProxyBech32Address: cfg.ScProxyBech32Address, - ExtraGasToExecute: cfg.ExtraGasToExecute, - MaxGasLimitToUse: cfg.MaxGasLimitToUse, - 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, + ScProxyBech32Address: cfg.ScProxyBech32Address, + 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) diff --git a/config/config.go b/config/config.go index 6c851ad7..cce4ce7d 100644 --- a/config/config.go +++ b/config/config.go @@ -192,20 +192,21 @@ type PendingOperationsFilterConfig struct { // ScCallsModuleConfig will hold the settings for the SC calls module type ScCallsModuleConfig struct { - ScProxyBech32Address string - ExtraGasToExecute uint64 - MaxGasLimitToUse uint64 - NetworkAddress string - ProxyMaxNoncesDelta int - ProxyFinalityCheck bool - ProxyCacherExpirationSeconds uint64 - ProxyRestAPIEntityType string - IntervalToResendTxsInSeconds uint64 - PrivateKeyFile string - PollingIntervalInMillis uint64 - Filter PendingOperationsFilterConfig - Logs LogsConfig - TransactionChecks TransactionChecksConfig + ScProxyBech32Address string + 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 } // 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 7ee85529..1dedb717 100644 --- a/config/tomlConfigs_test.go +++ b/config/tomlConfigs_test.go @@ -405,17 +405,18 @@ func TestScCallsExecutorConfigs(t *testing.T) { t.Parallel() expectedConfig := ScCallsModuleConfig{ - ScProxyBech32Address: "erd1qqqqqqqqqqqqqpgqnef5f5aq32d63kljld8w5vnvz4gk5sy9hrrq2ld08s", - ExtraGasToExecute: 50000000, - MaxGasLimitToUse: 249999999, - NetworkAddress: "127.0.0.1:8085", - ProxyMaxNoncesDelta: 7, - ProxyFinalityCheck: true, - ProxyCacherExpirationSeconds: 600, - ProxyRestAPIEntityType: "observer", - IntervalToResendTxsInSeconds: 60, - PrivateKeyFile: "keys/multiversx.pem", - PollingIntervalInMillis: 6000, + ScProxyBech32Address: "erd1qqqqqqqqqqqqqpgqnef5f5aq32d63kljld8w5vnvz4gk5sy9hrrq2ld08s", + 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{"*"}, @@ -438,6 +439,7 @@ func TestScCallsExecutorConfigs(t *testing.T) { ScProxyBech32Address = "erd1qqqqqqqqqqqqqpgqnef5f5aq32d63kljld8w5vnvz4gk5sy9hrrq2ld08s" 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 diff --git a/executors/multiversx/errors.go b/executors/multiversx/errors.go index 94e986c0..4b6339e9 100644 --- a/executors/multiversx/errors.go +++ b/executors/multiversx/errors.go @@ -3,16 +3,16 @@ package multiversx import "errors" var ( - errInvalidNumberOfResponseLines = errors.New("invalid number of responses") - errNilProxy = errors.New("nil proxy") - errNilCodec = errors.New("nil codec") - errNilFilter = errors.New("nil filter") - errNilLogger = errors.New("nil logger") - errNilNonceTxHandler = errors.New("nil nonce transaction handler") - errNilPrivateKey = errors.New("nil private key") - errNilSingleSigner = errors.New("nil single signer") - errInvalidValue = errors.New("invalid value") - errNilCloseAppChannel = errors.New("nil close application channel") - errTransactionFailed = errors.New("transaction failed") - errMaxGasLimitIsLessThanRequired = errors.New("max gas limit to execute a SC call is less than the minimum required") + errInvalidNumberOfResponseLines = errors.New("invalid number of responses") + errNilProxy = errors.New("nil proxy") + errNilCodec = errors.New("nil codec") + errNilFilter = errors.New("nil filter") + errNilLogger = errors.New("nil logger") + errNilNonceTxHandler = errors.New("nil nonce transaction handler") + errNilPrivateKey = errors.New("nil private key") + errNilSingleSigner = errors.New("nil single signer") + errInvalidValue = errors.New("invalid value") + errNilCloseAppChannel = errors.New("nil close application channel") + errTransactionFailed = errors.New("transaction failed") + errGasLimitIsLessThanAbsoluteMinimum = errors.New("provided gas limit is less than absolute minimum required") ) diff --git a/executors/multiversx/module/scCallsModule.go b/executors/multiversx/module/scCallsModule.go index 354396d3..19d2ff1b 100644 --- a/executors/multiversx/module/scCallsModule.go +++ b/executors/multiversx/module/scCallsModule.go @@ -73,18 +73,19 @@ func NewScCallsModule(cfg config.ScCallsModuleConfig, log logger.Logger, chClose } argsExecutor := multiversx.ArgsScCallExecutor{ - ScProxyBech32Address: cfg.ScProxyBech32Address, - Proxy: proxy, - Codec: &parsers.MultiversxCodec{}, - Filter: filter, - Log: log, - ExtraGasToExecute: cfg.ExtraGasToExecute, - MaxGasLimitToUse: cfg.MaxGasLimitToUse, - NonceTxHandler: module.nonceTxsHandler, - PrivateKey: privateKey, - SingleSigner: singleSigner, - CloseAppChan: chCloseApp, - TransactionChecks: cfg.TransactionChecks, + ScProxyBech32Address: cfg.ScProxyBech32Address, + 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, } module.executorInstance, err = multiversx.NewScCallExecutor(argsExecutor) if err != nil { diff --git a/executors/multiversx/module/scCallsModule_test.go b/executors/multiversx/module/scCallsModule_test.go index 4e9de029..246e25ea 100644 --- a/executors/multiversx/module/scCallsModule_test.go +++ b/executors/multiversx/module/scCallsModule_test.go @@ -11,17 +11,18 @@ import ( func createTestConfigs() config.ScCallsModuleConfig { return config.ScCallsModuleConfig{ - ScProxyBech32Address: "erd1qqqqqqqqqqqqqpgqgftcwj09u0nhmskrw7xxqcqh8qmzwyexd8ss7ftcxx", - ExtraGasToExecute: 6000000, - MaxGasLimitToUse: 249999999, - 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, + ScProxyBech32Address: "erd1qqqqqqqqqqqqqpgqgftcwj09u0nhmskrw7xxqcqh8qmzwyexd8ss7ftcxx", + 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{"*"}, @@ -98,6 +99,8 @@ func TestNewScCallsModule(t *testing.T) { assert.Nil(t, err) assert.NotNil(t, module) + assert.Zero(t, module.GetNumSentTransaction()) + err = module.Close() assert.Nil(t, err) }) @@ -113,6 +116,8 @@ func TestNewScCallsModule(t *testing.T) { assert.Nil(t, err) assert.NotNil(t, module) + assert.Zero(t, module.GetNumSentTransaction()) + err = module.Close() assert.Nil(t, err) }) diff --git a/executors/multiversx/scCallsExecutor.go b/executors/multiversx/scCallsExecutor.go index d6e307e1..a573c1c3 100644 --- a/executors/multiversx/scCallsExecutor.go +++ b/executors/multiversx/scCallsExecutor.go @@ -28,43 +28,46 @@ const ( minCheckValues = 1 transactionNotFoundErrString = "transaction not found" minGasToExecuteSCCalls = 2010000 // the absolut minimum gas limit to do a SC call + contractMaxGasLimit = 249999999 ) // ArgsScCallExecutor represents the DTO struct for creating a new instance of type scCallExecutor type ArgsScCallExecutor struct { - ScProxyBech32Address string - Proxy Proxy - Codec Codec - Filter ScCallsExecuteFilter - Log logger.Logger - ExtraGasToExecute uint64 - MaxGasLimitToUse uint64 - NonceTxHandler NonceTransactionsHandler - PrivateKey crypto.PrivateKey - SingleSigner crypto.SingleSigner - TransactionChecks config.TransactionChecksConfig - CloseAppChan chan struct{} + ScProxyBech32Address 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{} } type scCallExecutor struct { - scProxyBech32Address string - proxy Proxy - codec Codec - filter ScCallsExecuteFilter - log logger.Logger - extraGasToExecute uint64 - maxGasLimitToUse 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{} + scProxyBech32Address string + proxy Proxy + 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 @@ -82,23 +85,24 @@ func NewScCallExecutor(args ArgsScCallExecutor) (*scCallExecutor, error) { senderAddress := data.NewAddressFromBytes(publicKeyBytes) return &scCallExecutor{ - scProxyBech32Address: args.ScProxyBech32Address, - proxy: args.Proxy, - codec: args.Codec, - filter: args.Filter, - log: args.Log, - extraGasToExecute: args.ExtraGasToExecute, - maxGasLimitToUse: args.MaxGasLimitToUse, - nonceTxHandler: args.NonceTxHandler, - privateKey: args.PrivateKey, - singleSigner: args.SingleSigner, - senderAddress: senderAddress, - checkTransactionResults: args.TransactionChecks.CheckTransactionResults, - timeBetweenChecks: time.Second * time.Duration(args.TransactionChecks.TimeInSecondsBetweenChecks), - executionTimeout: time.Second * time.Duration(args.TransactionChecks.ExecutionTimeoutInSeconds), - closeAppOnError: args.TransactionChecks.CloseAppOnError, - extraDelayOnError: time.Second * time.Duration(args.TransactionChecks.ExtraDelayInSecondsOnError), - closeAppChan: args.CloseAppChan, + scProxyBech32Address: args.ScProxyBech32Address, + proxy: args.Proxy, + 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), + executionTimeout: time.Second * time.Duration(args.TransactionChecks.ExecutionTimeoutInSeconds), + closeAppOnError: args.TransactionChecks.CloseAppOnError, + extraDelayOnError: time.Second * time.Duration(args.TransactionChecks.ExtraDelayInSecondsOnError), + closeAppChan: args.CloseAppChan, }, nil } @@ -125,7 +129,10 @@ func checkArgs(args ArgsScCallExecutor) error { return errNilSingleSigner } if args.MaxGasLimitToUse < minGasToExecuteSCCalls { - return fmt.Errorf("%w: provided: %d, absolute minimum required: %d", errMaxGasLimitIsLessThanRequired, 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) } err := checkTransactionChecksConfig(args) if err != nil { @@ -287,8 +294,23 @@ func (executor *scCallExecutor) executeOperation( Value: "0", } + to, _ := callData.To.AddressAsBech32String() + if tx.GasLimit > 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, + "data", dataBytes, + "from", callData.From.Hex(), + "to", to, + "token", callData.Token, + "amount", callData.Amount, + "nonce", callData.Nonce, + ) + tx.GasLimit = executor.gasLimitForOutOfGasTransactions + } + if tx.GasLimit > executor.maxGasLimitToUse { - to, _ := callData.To.AddressAsBech32String() 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, @@ -324,7 +346,8 @@ func (executor *scCallExecutor) executeOperation( "tx ID", id, "call data", callData.String(), "extra gas", executor.extraGasToExecute, - "sender", bech32Address) + "sender", bech32Address, + "to", to) atomic.AddUint32(&executor.numSentTransactions, 1) diff --git a/executors/multiversx/scCallsExecutor_test.go b/executors/multiversx/scCallsExecutor_test.go index b0304595..bc9e8b30 100644 --- a/executors/multiversx/scCallsExecutor_test.go +++ b/executors/multiversx/scCallsExecutor_test.go @@ -1,6 +1,7 @@ package multiversx import ( + "bytes" "context" "encoding/hex" "errors" @@ -28,17 +29,18 @@ var testCodec = &testsCommon.TestMultiversXCodec{} func createMockArgsScCallExecutor() ArgsScCallExecutor { return ArgsScCallExecutor{ - ScProxyBech32Address: "erd1qqqqqqqqqqqqqpgqk839entmk46ykukvhpn90g6knskju3dtanaq20f66e", - Proxy: &interactors.ProxyStub{}, - Codec: &testsCommon.MultiversxCodecStub{}, - Filter: &testsCommon.ScCallsExecuteFilterStub{}, - Log: &testsCommon.LoggerStub{}, - ExtraGasToExecute: 100, - MaxGasLimitToUse: minGasToExecuteSCCalls, - NonceTxHandler: &testsCommon.TxNonceHandlerV2Stub{}, - PrivateKey: testCrypto.NewPrivateKeyMock(), - SingleSigner: &testCrypto.SingleSignerStub{}, - CloseAppChan: make(chan struct{}), + ScProxyBech32Address: "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{}), } } @@ -199,8 +201,22 @@ func TestNewScCallExecutor(t *testing.T) { executor, err := NewScCallExecutor(args) assert.Nil(t, executor) - assert.ErrorIs(t, err, errMaxGasLimitIsLessThanRequired) + assert.ErrorIs(t, err, errGasLimitIsLessThanAbsoluteMinimum) assert.Contains(t, err.Error(), "provided: 2009999, absolute minimum required: 2010000") + assert.Contains(t, err.Error(), "MaxGasLimitToUse") + }) + t.Run("invalid GasLimitForOutOfGasTransactions should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsScCallExecutor() + args.TransactionChecks = createMockCheckConfigs() + args.GasLimitForOutOfGasTransactions = minGasToExecuteSCCalls - 1 + + executor, err := NewScCallExecutor(args) + assert.Nil(t, executor) + assert.ErrorIs(t, err, errGasLimitIsLessThanAbsoluteMinimum) + assert.Contains(t, err.Error(), "provided: 2009999, absolute minimum required: 2010000") + assert.Contains(t, err.Error(), "GasLimitForOutOfGasTransactions") }) t.Run("should work without transaction checks", func(t *testing.T) { t.Parallel() @@ -330,7 +346,9 @@ func TestScCallExecutor_Execute(t *testing.T) { DecodeProxySCCompleteCallDataCalled: func(buff []byte) (parsers.ProxySCCompleteCallData, error) { assert.Equal(t, []byte{0x03, 0x04}, buff) - return parsers.ProxySCCompleteCallData{}, expectedError + return parsers.ProxySCCompleteCallData{ + To: data.NewAddressFromBytes(bytes.Repeat([]byte{1}, 32)), + }, expectedError }, } @@ -363,7 +381,9 @@ func TestScCallExecutor_Execute(t *testing.T) { DecodeProxySCCompleteCallDataCalled: func(buff []byte) (parsers.ProxySCCompleteCallData, error) { assert.Equal(t, []byte{0x03, 0x04}, buff) - return parsers.ProxySCCompleteCallData{}, nil + return parsers.ProxySCCompleteCallData{ + To: data.NewAddressFromBytes(bytes.Repeat([]byte{1}, 32)), + }, nil }, } @@ -405,7 +425,9 @@ func TestScCallExecutor_Execute(t *testing.T) { DecodeProxySCCompleteCallDataCalled: func(buff []byte) (parsers.ProxySCCompleteCallData, error) { assert.Equal(t, []byte{0x03, 0x04}, buff) - return parsers.ProxySCCompleteCallData{}, nil + return parsers.ProxySCCompleteCallData{ + To: data.NewAddressFromBytes(bytes.Repeat([]byte{1}, 32)), + }, nil }, } @@ -447,7 +469,9 @@ func TestScCallExecutor_Execute(t *testing.T) { DecodeProxySCCompleteCallDataCalled: func(buff []byte) (parsers.ProxySCCompleteCallData, error) { assert.Equal(t, []byte{0x03, 0x04}, buff) - return parsers.ProxySCCompleteCallData{}, nil + return parsers.ProxySCCompleteCallData{ + To: data.NewAddressFromBytes(bytes.Repeat([]byte{1}, 32)), + }, nil }, } args.SingleSigner = &testCrypto.SingleSignerStub{ @@ -495,6 +519,7 @@ func TestScCallExecutor_Execute(t *testing.T) { return parsers.ProxySCCompleteCallData{ RawCallData: []byte("dummy"), + To: data.NewAddressFromBytes(bytes.Repeat([]byte{1}, 32)), }, nil }, ExtractGasLimitFromRawCallDataCalled: func(buff []byte) (uint64, error) { @@ -560,7 +585,9 @@ func TestScCallExecutor_Execute(t *testing.T) { return createTestProxySCCompleteCallData("tkn2"), nil } - return parsers.ProxySCCompleteCallData{}, errors.New("wrong buffer") + return parsers.ProxySCCompleteCallData{ + To: data.NewAddressFromBytes(bytes.Repeat([]byte{1}, 32)), + }, errors.New("wrong buffer") }, ExtractGasLimitFromRawCallDataCalled: func(buff []byte) (uint64, error) { return 5000000, nil @@ -706,6 +733,99 @@ func TestScCallExecutor_Execute(t *testing.T) { 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{ + ExecuteVMQueryCalled: func(ctx context.Context, vmRequest *data.VmValueRequest) (*data.VmValuesResponseData, error) { + assert.Equal(t, args.ScProxyBech32Address, 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 contractMaxGasLimit + 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 { + 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 gor through the filter + expectedData := scProxyCallFunction + "@02" + assert.Equal(t, expectedData, string(tx.Data)) + + 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() diff --git a/integrationTests/relayers/slowTests/framework/testSetup.go b/integrationTests/relayers/slowTests/framework/testSetup.go index a7ab3b2f..d35232a9 100644 --- a/integrationTests/relayers/slowTests/framework/testSetup.go +++ b/integrationTests/relayers/slowTests/framework/testSetup.go @@ -112,17 +112,18 @@ func (setup *TestSetup) StartRelayersAndScModule() { func (setup *TestSetup) startScCallerModule() { cfg := config.ScCallsModuleConfig{ - ScProxyBech32Address: setup.MultiversxHandler.ScProxyAddress.Bech32(), - 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 - 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 + ScProxyBech32Address: setup.MultiversxHandler.ScProxyAddress.Bech32(), + 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{"*"},