diff --git a/bridges/ethMultiversX/interface.go b/bridges/ethMultiversX/interface.go index 9e459101..d514d171 100644 --- a/bridges/ethMultiversX/interface.go +++ b/bridges/ethMultiversX/interface.go @@ -24,6 +24,7 @@ type MultiversXClient interface { GetActionIDForSetStatusOnPendingTransfer(ctx context.Context, batch *bridgeCore.TransferBatch) (uint64, error) GetLastExecutedEthBatchID(ctx context.Context) (uint64, error) GetLastExecutedEthTxID(ctx context.Context) (uint64, error) + GetLastMvxBatchID(ctx context.Context) (uint64, error) GetCurrentNonce(ctx context.Context) (uint64, error) ProposeSetStatus(ctx context.Context, batch *bridgeCore.TransferBatch) (string, error) diff --git a/clients/balanceValidator/balanceValidator.go b/clients/balanceValidator/balanceValidator.go index a912365c..0824dc41 100644 --- a/clients/balanceValidator/balanceValidator.go +++ b/clients/balanceValidator/balanceValidator.go @@ -112,8 +112,6 @@ func (validator *balanceValidator) CheckToken(ctx context.Context, ethToken comm "amount", amount.String(), ) - // TODO(next PRs): fix here to not consider the pending batch in the mvx->eth direction that executed on eth. - if ethAmount.Cmp(mvxAmount) != 0 { return fmt.Errorf("%w, balance for ERC20 token %s is %s and the balance for ESDT token %s is %s, direction %s", ErrBalanceMismatch, ethToken.String(), ethAmount.String(), mvxToken, mvxAmount.String(), direction) @@ -271,21 +269,13 @@ func getTotalAmountFromBatch(batch *bridgeCore.TransferBatch, token []byte) *big } func (validator *balanceValidator) getTotalTransferAmountInPendingMvxBatches(ctx context.Context, mvxToken []byte) (*big.Int, error) { - batch, err := validator.multiversXClient.GetPendingBatch(ctx) - if errors.Is(err, clients.ErrNoPendingBatchAvailable) { - return big.NewInt(0), nil - } + batchID, err := validator.multiversXClient.GetLastMvxBatchID(ctx) if err != nil { return nil, err } - // check if the pending batch is executed on Ethereum and is not final + var batch *bridgeCore.TransferBatch amount := big.NewInt(0) - if validator.batchExecutedAndNotFinalOnEth(ctx, batch.ID) { - amount.Add(amount, getTotalAmountFromBatch(batch, mvxToken)) - } - - batchID := batch.ID + 1 for { batch, err = validator.multiversXClient.GetBatch(ctx, batchID) if errors.Is(err, clients.ErrNoBatchAvailable) { @@ -295,18 +285,20 @@ func (validator *balanceValidator) getTotalTransferAmountInPendingMvxBatches(ctx return nil, err } + wasExecuted, errWasExecuted := validator.ethereumClient.WasExecuted(ctx, batch.ID) + if errWasExecuted != nil { + return nil, errWasExecuted + } + if wasExecuted { + return amount, nil + } + amountFromBatch := getTotalAmountFromBatch(batch, mvxToken) amount.Add(amount, amountFromBatch) - batchID++ + batchID-- // go to the previous batch } } -func (validator *balanceValidator) batchExecutedAndNotFinalOnEth(ctx context.Context, nonce uint64) bool { - // TODO: analyze if we need to check the statuses returned - _, err := validator.ethereumClient.GetTransactionsStatuses(ctx, nonce) - return err != nil -} - func (validator *balanceValidator) getTotalTransferAmountInPendingEthBatches(ctx context.Context, ethToken common.Address) (*big.Int, error) { batchID, err := validator.multiversXClient.GetLastExecutedEthBatchID(ctx) if err != nil { diff --git a/clients/balanceValidator/balanceValidator_test.go b/clients/balanceValidator/balanceValidator_test.go index d22c7f57..2f823872 100644 --- a/clients/balanceValidator/balanceValidator_test.go +++ b/clients/balanceValidator/balanceValidator_test.go @@ -36,7 +36,6 @@ type testConfiguration struct { totalBalancesOnEth *big.Int burnBalancesOnEth *big.Int mintBalancesOnEth *big.Int - isFinalOnEth bool isNativeOnMvx bool isMintBurnOnMvx bool @@ -61,7 +60,6 @@ func (cfg *testConfiguration) deepClone() testConfiguration { result := testConfiguration{ isNativeOnEth: cfg.isNativeOnEth, isMintBurnOnEth: cfg.isMintBurnOnEth, - isFinalOnEth: cfg.isFinalOnEth, isNativeOnMvx: cfg.isNativeOnMvx, isMintBurnOnMvx: cfg.isMintBurnOnMvx, errorsOnCalls: make(map[string]error), @@ -179,8 +177,7 @@ func TestBridgeExecutor_CheckToken(t *testing.T) { t.Parallel() cfg := testConfiguration{ - direction: "", - isFinalOnEth: true, + direction: "", } result := validatorTester(cfg) assert.ErrorIs(t, result.error, ErrInvalidDirection) @@ -190,8 +187,7 @@ func TestBridgeExecutor_CheckToken(t *testing.T) { t.Run("on isMintBurnOnEthereum", func(t *testing.T) { cfg := testConfiguration{ - direction: batchProcessor.FromMultiversX, - isFinalOnEth: true, + direction: batchProcessor.FromMultiversX, errorsOnCalls: map[string]error{ "MintBurnTokensEth": expectedError, }, @@ -203,8 +199,7 @@ func TestBridgeExecutor_CheckToken(t *testing.T) { }) t.Run("on isMintBurnOnMultiversX", func(t *testing.T) { cfg := testConfiguration{ - direction: batchProcessor.ToMultiversX, - isFinalOnEth: true, + direction: batchProcessor.ToMultiversX, errorsOnCalls: map[string]error{ "IsMintBurnTokenMvx": expectedError, }, @@ -216,8 +211,7 @@ func TestBridgeExecutor_CheckToken(t *testing.T) { }) t.Run("on isNativeOnEthereum", func(t *testing.T) { cfg := testConfiguration{ - direction: batchProcessor.ToMultiversX, - isFinalOnEth: true, + direction: batchProcessor.ToMultiversX, errorsOnCalls: map[string]error{ "NativeTokensEth": expectedError, }, @@ -229,8 +223,7 @@ func TestBridgeExecutor_CheckToken(t *testing.T) { }) t.Run("on isNativeOnMultiversX", func(t *testing.T) { cfg := testConfiguration{ - direction: batchProcessor.FromMultiversX, - isFinalOnEth: true, + direction: batchProcessor.FromMultiversX, errorsOnCalls: map[string]error{ "IsNativeTokenMvx": expectedError, }, @@ -245,7 +238,6 @@ func TestBridgeExecutor_CheckToken(t *testing.T) { direction: batchProcessor.FromMultiversX, isMintBurnOnMvx: true, isNativeOnEth: true, - isFinalOnEth: true, errorsOnCalls: map[string]error{ "TotalBalancesEth": expectedError, }, @@ -260,7 +252,6 @@ func TestBridgeExecutor_CheckToken(t *testing.T) { direction: batchProcessor.FromMultiversX, isNativeOnMvx: true, isMintBurnOnEth: true, - isFinalOnEth: true, errorsOnCalls: map[string]error{ "BurnBalancesEth": expectedError, }, @@ -275,7 +266,6 @@ func TestBridgeExecutor_CheckToken(t *testing.T) { direction: batchProcessor.FromMultiversX, isNativeOnMvx: true, isMintBurnOnEth: true, - isFinalOnEth: true, errorsOnCalls: map[string]error{ "MintBalancesEth": expectedError, }, @@ -290,7 +280,6 @@ func TestBridgeExecutor_CheckToken(t *testing.T) { direction: batchProcessor.FromMultiversX, isNativeOnMvx: true, isMintBurnOnEth: true, - isFinalOnEth: true, errorsOnCalls: map[string]error{ "GetLastExecutedEthBatchIDMvx": expectedError, }, @@ -305,7 +294,6 @@ func TestBridgeExecutor_CheckToken(t *testing.T) { direction: batchProcessor.FromMultiversX, isNativeOnMvx: true, isMintBurnOnEth: true, - isFinalOnEth: true, errorsOnCalls: map[string]error{ "GetBatchEth": expectedError, }, @@ -320,7 +308,6 @@ func TestBridgeExecutor_CheckToken(t *testing.T) { direction: batchProcessor.ToMultiversX, isNativeOnMvx: true, isMintBurnOnEth: true, - isFinalOnEth: true, errorsOnCalls: map[string]error{ "TotalBalancesMvx": expectedError, }, @@ -335,7 +322,6 @@ func TestBridgeExecutor_CheckToken(t *testing.T) { direction: batchProcessor.ToMultiversX, isMintBurnOnMvx: true, isNativeOnEth: true, - isFinalOnEth: true, errorsOnCalls: map[string]error{ "BurnBalancesMvx": expectedError, }, @@ -350,7 +336,6 @@ func TestBridgeExecutor_CheckToken(t *testing.T) { direction: batchProcessor.ToMultiversX, isMintBurnOnMvx: true, isNativeOnEth: true, - isFinalOnEth: true, errorsOnCalls: map[string]error{ "MintBalancesMvx": expectedError, }, @@ -360,14 +345,13 @@ func TestBridgeExecutor_CheckToken(t *testing.T) { assert.False(t, result.checkRequiredBalanceOnEthCalled) assert.True(t, result.checkRequiredBalanceOnMvxCalled) }) - t.Run("on computeMvxAmount, GetPendingBatch", func(t *testing.T) { + t.Run("on computeMvxAmount, GetLastMvxBatchID", func(t *testing.T) { cfg := testConfiguration{ direction: batchProcessor.ToMultiversX, isMintBurnOnMvx: true, isNativeOnEth: true, - isFinalOnEth: true, errorsOnCalls: map[string]error{ - "GetPendingBatchMvx": expectedError, + "GetLastMvxBatchID": expectedError, }, } result := validatorTester(cfg) @@ -380,7 +364,6 @@ func TestBridgeExecutor_CheckToken(t *testing.T) { direction: batchProcessor.ToMultiversX, isMintBurnOnMvx: true, isNativeOnEth: true, - isFinalOnEth: true, errorsOnCalls: map[string]error{ "GetBatchMvx": expectedError, }, @@ -390,6 +373,20 @@ func TestBridgeExecutor_CheckToken(t *testing.T) { assert.False(t, result.checkRequiredBalanceOnEthCalled) assert.True(t, result.checkRequiredBalanceOnMvxCalled) }) + t.Run("on computeMvxAmount, WasExecuted", func(t *testing.T) { + cfg := testConfiguration{ + direction: batchProcessor.ToMultiversX, + isMintBurnOnMvx: true, + isNativeOnEth: true, + errorsOnCalls: map[string]error{ + "WasExecutedEth": expectedError, + }, + } + result := validatorTester(cfg) + assert.Equal(t, expectedError, result.error) + assert.False(t, result.checkRequiredBalanceOnEthCalled) + assert.True(t, result.checkRequiredBalanceOnMvxCalled) + }) }) t.Run("invalid setup", func(t *testing.T) { t.Parallel() @@ -398,7 +395,6 @@ func TestBridgeExecutor_CheckToken(t *testing.T) { cfg := testConfiguration{ direction: batchProcessor.ToMultiversX, isMintBurnOnMvx: true, - isFinalOnEth: true, } result := validatorTester(cfg) assert.ErrorIs(t, result.error, ErrInvalidSetup) @@ -410,7 +406,6 @@ func TestBridgeExecutor_CheckToken(t *testing.T) { cfg := testConfiguration{ direction: batchProcessor.ToMultiversX, isNativeOnEth: true, - isFinalOnEth: true, } result := validatorTester(cfg) assert.ErrorIs(t, result.error, ErrInvalidSetup) @@ -422,7 +417,6 @@ func TestBridgeExecutor_CheckToken(t *testing.T) { cfg := testConfiguration{ direction: batchProcessor.ToMultiversX, isNativeOnEth: true, - isFinalOnEth: true, isNativeOnMvx: true, } result := validatorTester(cfg) @@ -442,7 +436,6 @@ func TestBridgeExecutor_CheckToken(t *testing.T) { direction: batchProcessor.ToMultiversX, isMintBurnOnEth: true, isNativeOnEth: true, - isFinalOnEth: true, isMintBurnOnMvx: true, burnBalancesOnEth: big.NewInt(37), mintBalancesOnEth: big.NewInt(38), @@ -459,7 +452,6 @@ func TestBridgeExecutor_CheckToken(t *testing.T) { cfg := testConfiguration{ direction: batchProcessor.ToMultiversX, isMintBurnOnEth: true, - isFinalOnEth: true, isNativeOnMvx: true, burnBalancesOnEth: big.NewInt(38), mintBalancesOnEth: big.NewInt(37), @@ -476,7 +468,6 @@ func TestBridgeExecutor_CheckToken(t *testing.T) { cfg := testConfiguration{ direction: batchProcessor.ToMultiversX, isMintBurnOnEth: true, - isFinalOnEth: true, isMintBurnOnMvx: true, isNativeOnMvx: true, burnBalancesOnMvx: big.NewInt(37), @@ -494,7 +485,6 @@ func TestBridgeExecutor_CheckToken(t *testing.T) { cfg := testConfiguration{ direction: batchProcessor.ToMultiversX, isNativeOnEth: true, - isFinalOnEth: true, isMintBurnOnMvx: true, burnBalancesOnMvx: big.NewInt(38), mintBalancesOnMvx: big.NewInt(37), @@ -518,7 +508,6 @@ func TestBridgeExecutor_CheckToken(t *testing.T) { cfg := testConfiguration{ direction: batchProcessor.ToMultiversX, isMintBurnOnEth: true, - isFinalOnEth: true, isNativeOnMvx: true, burnBalancesOnEth: big.NewInt(1100), // initial burn (1000) + burn from this transfer (100) mintBalancesOnEth: big.NewInt(11000), // minted (10000) + initial burn (1000) @@ -548,7 +537,6 @@ func TestBridgeExecutor_CheckToken(t *testing.T) { cfg := testConfiguration{ direction: batchProcessor.ToMultiversX, isMintBurnOnEth: true, - isFinalOnEth: true, isNativeOnMvx: true, burnBalancesOnEth: big.NewInt(1220), // initial burn (1000) + burn from this transfer (100) + burn from next batches (120) mintBalancesOnEth: big.NewInt(11000), // minted (10000) + initial burn (1000) @@ -580,7 +568,6 @@ func TestBridgeExecutor_CheckToken(t *testing.T) { cfg := testConfiguration{ direction: batchProcessor.ToMultiversX, isMintBurnOnEth: true, - isFinalOnEth: true, isNativeOnMvx: true, isMintBurnOnMvx: true, burnBalancesOnEth: big.NewInt(1100), // initial burn (1000) + burn from this transfer (100) @@ -612,7 +599,6 @@ func TestBridgeExecutor_CheckToken(t *testing.T) { cfg := testConfiguration{ direction: batchProcessor.ToMultiversX, isMintBurnOnEth: true, - isFinalOnEth: true, isNativeOnMvx: true, isMintBurnOnMvx: true, burnBalancesOnEth: big.NewInt(1220), // initial burn (1000) + burn from this transfer (100) + next batches (120) @@ -647,7 +633,6 @@ func TestBridgeExecutor_CheckToken(t *testing.T) { direction: batchProcessor.ToMultiversX, isMintBurnOnMvx: true, isNativeOnEth: true, - isFinalOnEth: true, burnBalancesOnMvx: big.NewInt(1000), // initial burn (1000) mintBalancesOnMvx: big.NewInt(11000), // minted (10000) + initial burn (1000) totalBalancesOnEth: big.NewInt(10100), // initial (10000) + locked from this transfer (100) @@ -677,7 +662,6 @@ func TestBridgeExecutor_CheckToken(t *testing.T) { direction: batchProcessor.ToMultiversX, isMintBurnOnMvx: true, isNativeOnEth: true, - isFinalOnEth: true, burnBalancesOnMvx: big.NewInt(1000), // initial burn (1000) mintBalancesOnMvx: big.NewInt(11000), // minted (10000) + initial burn (1000) totalBalancesOnEth: big.NewInt(10220), // initial (10000) + locked from this transfer (100) + next batches (120) @@ -710,7 +694,6 @@ func TestBridgeExecutor_CheckToken(t *testing.T) { isMintBurnOnMvx: true, isNativeOnEth: true, isMintBurnOnEth: true, - isFinalOnEth: true, burnBalancesOnMvx: big.NewInt(1000), // initial burn (1000) mintBalancesOnMvx: big.NewInt(11000), // minted (10000) + initial burn (1000) burnBalancesOnEth: big.NewInt(12100), @@ -742,7 +725,6 @@ func TestBridgeExecutor_CheckToken(t *testing.T) { isMintBurnOnMvx: true, isNativeOnEth: true, isMintBurnOnEth: true, - isFinalOnEth: true, burnBalancesOnMvx: big.NewInt(1000), // initial burn (1000) mintBalancesOnMvx: big.NewInt(11000), // minted (10000) + initial burn (1000) burnBalancesOnEth: big.NewInt(12220), @@ -779,7 +761,6 @@ func TestBridgeExecutor_CheckToken(t *testing.T) { cfg := testConfiguration{ direction: batchProcessor.FromMultiversX, isMintBurnOnEth: true, - isFinalOnEth: true, isNativeOnMvx: true, burnBalancesOnEth: big.NewInt(1000), // initial burn (1000) mintBalancesOnEth: big.NewInt(11000), // minted (10000) + initial burn (1000) @@ -809,7 +790,6 @@ func TestBridgeExecutor_CheckToken(t *testing.T) { cfg := testConfiguration{ direction: batchProcessor.FromMultiversX, isMintBurnOnEth: true, - isFinalOnEth: true, isNativeOnMvx: true, burnBalancesOnEth: big.NewInt(1000), // initial burn (1000) mintBalancesOnEth: big.NewInt(11000), // minted (10000) + initial burn (1000) @@ -841,7 +821,6 @@ func TestBridgeExecutor_CheckToken(t *testing.T) { cfg := testConfiguration{ direction: batchProcessor.FromMultiversX, isMintBurnOnEth: true, - isFinalOnEth: true, isNativeOnMvx: true, isMintBurnOnMvx: true, burnBalancesOnEth: big.NewInt(1000), // initial burn (1000) @@ -873,7 +852,6 @@ func TestBridgeExecutor_CheckToken(t *testing.T) { cfg := testConfiguration{ direction: batchProcessor.FromMultiversX, isMintBurnOnEth: true, - isFinalOnEth: true, isNativeOnMvx: true, isMintBurnOnMvx: true, burnBalancesOnEth: big.NewInt(1000), // initial burn (1000) @@ -908,7 +886,6 @@ func TestBridgeExecutor_CheckToken(t *testing.T) { direction: batchProcessor.FromMultiversX, isMintBurnOnMvx: true, isNativeOnEth: true, - isFinalOnEth: true, burnBalancesOnMvx: big.NewInt(1100), // initial burn (1000) + transfer from this batch (100) mintBalancesOnMvx: big.NewInt(11000), // minted (10000) + initial burn (1000) totalBalancesOnEth: big.NewInt(10000), // initial (10000) @@ -938,7 +915,6 @@ func TestBridgeExecutor_CheckToken(t *testing.T) { direction: batchProcessor.FromMultiversX, isMintBurnOnMvx: true, isNativeOnEth: true, - isFinalOnEth: true, burnBalancesOnMvx: big.NewInt(1220), // initial burn (1000) + transfer from this batch (100) + next batches (120) mintBalancesOnMvx: big.NewInt(11000), // minted (10000) + initial burn (1000) totalBalancesOnEth: big.NewInt(10000), // initial (10000) @@ -971,7 +947,6 @@ func TestBridgeExecutor_CheckToken(t *testing.T) { isMintBurnOnMvx: true, isNativeOnEth: true, isMintBurnOnEth: true, - isFinalOnEth: true, burnBalancesOnMvx: big.NewInt(1100), // initial burn (1000) + transfer from this batch (100) mintBalancesOnMvx: big.NewInt(11000), // minted (10000) + initial burn (1000) burnBalancesOnEth: big.NewInt(12000), @@ -1003,7 +978,6 @@ func TestBridgeExecutor_CheckToken(t *testing.T) { isMintBurnOnMvx: true, isNativeOnEth: true, isMintBurnOnEth: true, - isFinalOnEth: true, burnBalancesOnMvx: big.NewInt(1220), // initial burn (1000) + transfer from this batch (100) + transfer from next batches mintBalancesOnMvx: big.NewInt(11000), // minted (10000) + initial burn (1000) burnBalancesOnEth: big.NewInt(12000), @@ -1045,10 +1019,9 @@ func TestBridgeExecutor_CheckToken(t *testing.T) { direction: batchProcessor.ToMultiversX, isMintBurnOnEth: true, isNativeOnMvx: true, - isFinalOnEth: true, burnBalancesOnEth: big.NewInt(existingBurnEth + 100 + 30 + 40 + 50), mintBalancesOnEth: big.NewInt(existingMintEth), - totalBalancesOnMvx: big.NewInt(existingNativeBalanceMvx + 60 + 80 + 100), // the current pending batch value (amount2) is not accounted + totalBalancesOnMvx: big.NewInt(existingNativeBalanceMvx + 60 + 80 + 100 + 200), amount: amount, pendingMvxBatchId: 1, amountsOnEthPendingBatches: map[uint64][]*big.Int{ @@ -1076,15 +1049,6 @@ func TestBridgeExecutor_CheckToken(t *testing.T) { result = validatorTester(copiedCfg) assert.ErrorIs(t, result.error, ErrBalanceMismatch) }) - t.Run("non final batch on eth", func(t *testing.T) { - copiedCfg := cfg.deepClone() - copiedCfg.isFinalOnEth = false - copiedCfg.totalBalancesOnMvx.Add(copiedCfg.totalBalancesOnMvx, amount2) // the current pending batch value (amount2) should be accounted - result = validatorTester(copiedCfg) - assert.Nil(t, result.error) - assert.False(t, result.checkRequiredBalanceOnEthCalled) - assert.True(t, result.checkRequiredBalanceOnMvxCalled) - }) }) t.Run("from Eth: native on MvX but with mint-burn, mint-burn on Eth, ok values, with next pending batches", func(t *testing.T) { t.Parallel() @@ -1099,10 +1063,9 @@ func TestBridgeExecutor_CheckToken(t *testing.T) { isMintBurnOnEth: true, isNativeOnMvx: true, isMintBurnOnMvx: true, - isFinalOnEth: true, burnBalancesOnEth: big.NewInt(existingBurnEth + 100 + 30 + 40 + 50), mintBalancesOnEth: big.NewInt(existingMintEth), - burnBalancesOnMvx: big.NewInt(existingBurnMvx + 60 + 80 + 100), // the current pending batch value (amount2) is not accounted + burnBalancesOnMvx: big.NewInt(existingBurnMvx + 60 + 80 + 100 + 200), mintBalancesOnMvx: big.NewInt(existingMintMvx), amount: amount, pendingMvxBatchId: 1, @@ -1131,15 +1094,6 @@ func TestBridgeExecutor_CheckToken(t *testing.T) { result = validatorTester(copiedCfg) assert.ErrorIs(t, result.error, ErrBalanceMismatch) }) - t.Run("non final batch on eth", func(t *testing.T) { - copiedCfg := cfg.deepClone() - copiedCfg.isFinalOnEth = false - copiedCfg.burnBalancesOnMvx.Add(copiedCfg.burnBalancesOnMvx, amount2) // the current pending batch value (amount2) should be accounted - result = validatorTester(copiedCfg) - assert.Nil(t, result.error) - assert.False(t, result.checkRequiredBalanceOnEthCalled) - assert.True(t, result.checkRequiredBalanceOnMvxCalled) - }) }) t.Run("from Eth: native on Eth, mint-burn on MvX, ok values, with next pending batches", func(t *testing.T) { t.Parallel() @@ -1152,10 +1106,9 @@ func TestBridgeExecutor_CheckToken(t *testing.T) { direction: batchProcessor.ToMultiversX, isMintBurnOnMvx: true, isNativeOnEth: true, - isFinalOnEth: true, burnBalancesOnMvx: big.NewInt(existingBurnMvx + 200 + 60 + 80 + 100), mintBalancesOnMvx: big.NewInt(existingMintMvx), - totalBalancesOnEth: big.NewInt(existingNativeBalanceEth + 100 + 30 + 40 + 50 - 200), // initial + locked from this transfer + next batches - amount2 + totalBalancesOnEth: big.NewInt(existingNativeBalanceEth + 100 + 30 + 40 + 50), amount: amount, pendingMvxBatchId: 1, amountsOnEthPendingBatches: map[uint64][]*big.Int{ @@ -1183,15 +1136,6 @@ func TestBridgeExecutor_CheckToken(t *testing.T) { result = validatorTester(copiedCfg) assert.ErrorIs(t, result.error, ErrBalanceMismatch) }) - t.Run("non final batch on eth", func(t *testing.T) { - copiedCfg := cfg.deepClone() - copiedCfg.isFinalOnEth = false - copiedCfg.totalBalancesOnEth = big.NewInt(existingNativeBalanceEth + 100 + 30 + 40 + 50) // the current pending batch value (amount2) should be accounted - result = validatorTester(copiedCfg) - assert.Nil(t, result.error) - assert.False(t, result.checkRequiredBalanceOnEthCalled) - assert.True(t, result.checkRequiredBalanceOnMvxCalled) - }) }) t.Run("from Eth: native on Eth but with mint-burn, mint-burn on MvX, ok values, with next pending batches", func(t *testing.T) { t.Parallel() @@ -1206,11 +1150,10 @@ func TestBridgeExecutor_CheckToken(t *testing.T) { isMintBurnOnMvx: true, isNativeOnEth: true, isMintBurnOnEth: true, - isFinalOnEth: true, burnBalancesOnMvx: big.NewInt(existingBurnMvx + 200 + 60 + 80 + 100), mintBalancesOnMvx: big.NewInt(existingMintMvx), burnBalancesOnEth: big.NewInt(existingBurnEth + 100 + 30 + 40 + 50), - mintBalancesOnEth: big.NewInt(existingMintEth + 200), // we should add amount 2 here + mintBalancesOnEth: big.NewInt(existingMintEth), amount: amount, pendingMvxBatchId: 1, amountsOnEthPendingBatches: map[uint64][]*big.Int{ @@ -1238,15 +1181,6 @@ func TestBridgeExecutor_CheckToken(t *testing.T) { result = validatorTester(copiedCfg) assert.ErrorIs(t, result.error, ErrBalanceMismatch) }) - t.Run("non final batch on eth", func(t *testing.T) { - copiedCfg := cfg.deepClone() - copiedCfg.isFinalOnEth = false - copiedCfg.mintBalancesOnEth = big.NewInt(existingMintEth) // the burn balance was not yet updated with amount2 - result = validatorTester(copiedCfg) - assert.Nil(t, result.error) - assert.False(t, result.checkRequiredBalanceOnEthCalled) - assert.True(t, result.checkRequiredBalanceOnMvxCalled) - }) }) t.Run("from MvX: native on MvX, mint-burn on Eth, ok values, with next pending batches on both chains", func(t *testing.T) { t.Parallel() @@ -1259,10 +1193,9 @@ func TestBridgeExecutor_CheckToken(t *testing.T) { direction: batchProcessor.FromMultiversX, isMintBurnOnEth: true, isNativeOnMvx: true, - isFinalOnEth: true, burnBalancesOnEth: big.NewInt(existingBurnEth + 200 + 60 + 80 + 100), mintBalancesOnEth: big.NewInt(existingMintEth), - totalBalancesOnMvx: big.NewInt(existingNativeBalanceMvx + 30 + 40 + 50), // the current pending batch value (amount) is not accounted + totalBalancesOnMvx: big.NewInt(existingNativeBalanceMvx + 30 + 40 + 50 + 100), amount: amount, pendingMvxBatchId: 1, amountsOnMvxPendingBatches: map[uint64][]*big.Int{ @@ -1290,15 +1223,6 @@ func TestBridgeExecutor_CheckToken(t *testing.T) { result = validatorTester(copiedCfg) assert.ErrorIs(t, result.error, ErrBalanceMismatch) }) - t.Run("non final batch on eth", func(t *testing.T) { - copiedCfg := cfg.deepClone() - copiedCfg.isFinalOnEth = false - copiedCfg.totalBalancesOnMvx.Add(copiedCfg.totalBalancesOnMvx, amount) // the current pending batch value (amount) should be accounted - result = validatorTester(copiedCfg) - assert.Nil(t, result.error) - assert.True(t, result.checkRequiredBalanceOnEthCalled) - assert.False(t, result.checkRequiredBalanceOnMvxCalled) - }) }) t.Run("from MvX: native on MvX but with mint-burn, mint-burn on Eth, ok values, with next pending batches", func(t *testing.T) { t.Parallel() @@ -1313,10 +1237,9 @@ func TestBridgeExecutor_CheckToken(t *testing.T) { isMintBurnOnEth: true, isNativeOnMvx: true, isMintBurnOnMvx: true, - isFinalOnEth: true, burnBalancesOnEth: big.NewInt(existingBurnEth + 200 + 60 + 80 + 100), mintBalancesOnEth: big.NewInt(existingMintEth), - burnBalancesOnMvx: big.NewInt(existingBurnMvx + 30 + 40 + 50), // the current pending batch value (amount) is not accounted + burnBalancesOnMvx: big.NewInt(existingBurnMvx + 30 + 40 + 50 + 100), mintBalancesOnMvx: big.NewInt(existingMintMvx), amount: amount, pendingMvxBatchId: 1, @@ -1345,15 +1268,6 @@ func TestBridgeExecutor_CheckToken(t *testing.T) { result = validatorTester(copiedCfg) assert.ErrorIs(t, result.error, ErrBalanceMismatch) }) - t.Run("non final batch on eth", func(t *testing.T) { - copiedCfg := cfg.deepClone() - copiedCfg.isFinalOnEth = false - copiedCfg.burnBalancesOnMvx.Add(copiedCfg.burnBalancesOnMvx, amount) // the current pending batch value (amount) should be accounted - result = validatorTester(copiedCfg) - assert.Nil(t, result.error) - assert.True(t, result.checkRequiredBalanceOnEthCalled) - assert.False(t, result.checkRequiredBalanceOnMvxCalled) - }) }) t.Run("from MvX: native on Eth, mint-burn on MvX, ok values, with next pending batches", func(t *testing.T) { t.Parallel() @@ -1366,10 +1280,9 @@ func TestBridgeExecutor_CheckToken(t *testing.T) { direction: batchProcessor.FromMultiversX, isMintBurnOnMvx: true, isNativeOnEth: true, - isFinalOnEth: true, burnBalancesOnMvx: big.NewInt(existingBurnMvx + 100 + 30 + 40 + 50), mintBalancesOnMvx: big.NewInt(existingMintMvx), - totalBalancesOnEth: big.NewInt(existingNativeBalanceEth + 200 + 60 + 80 + 100 - 100), // initial + locked from this transfer + next batches - amount + totalBalancesOnEth: big.NewInt(existingNativeBalanceEth + 200 + 60 + 80 + 100), amount: amount, pendingMvxBatchId: 1, amountsOnMvxPendingBatches: map[uint64][]*big.Int{ @@ -1397,15 +1310,6 @@ func TestBridgeExecutor_CheckToken(t *testing.T) { result = validatorTester(copiedCfg) assert.ErrorIs(t, result.error, ErrBalanceMismatch) }) - t.Run("non final batch on eth", func(t *testing.T) { - copiedCfg := cfg.deepClone() - copiedCfg.isFinalOnEth = false - copiedCfg.totalBalancesOnEth = big.NewInt(existingNativeBalanceEth + 200 + 60 + 80 + 100) // the current pending batch value (amount) should be accounted - result = validatorTester(copiedCfg) - assert.Nil(t, result.error) - assert.True(t, result.checkRequiredBalanceOnEthCalled) - assert.False(t, result.checkRequiredBalanceOnMvxCalled) - }) }) t.Run("from MvX: native on Eth but with mint-burn, mint-burn on MvX, ok values, with next pending batches", func(t *testing.T) { t.Parallel() @@ -1420,11 +1324,10 @@ func TestBridgeExecutor_CheckToken(t *testing.T) { isMintBurnOnMvx: true, isNativeOnEth: true, isMintBurnOnEth: true, - isFinalOnEth: true, burnBalancesOnMvx: big.NewInt(existingBurnMvx + 100 + 30 + 40 + 50), mintBalancesOnMvx: big.NewInt(existingMintMvx), burnBalancesOnEth: big.NewInt(existingBurnEth + 200 + 60 + 80 + 100), - mintBalancesOnEth: big.NewInt(existingMintEth + 100), // we should add amount here + mintBalancesOnEth: big.NewInt(existingMintEth), amount: amount, pendingMvxBatchId: 1, amountsOnMvxPendingBatches: map[uint64][]*big.Int{ @@ -1452,18 +1355,8 @@ func TestBridgeExecutor_CheckToken(t *testing.T) { result = validatorTester(copiedCfg) assert.ErrorIs(t, result.error, ErrBalanceMismatch) }) - t.Run("non final batch on eth", func(t *testing.T) { - copiedCfg := cfg.deepClone() - copiedCfg.isFinalOnEth = false - copiedCfg.mintBalancesOnEth = big.NewInt(existingMintEth) // the burn balance was not yet updated with amount - result = validatorTester(copiedCfg) - assert.Nil(t, result.error) - assert.True(t, result.checkRequiredBalanceOnEthCalled) - assert.False(t, result.checkRequiredBalanceOnMvxCalled) - }) }) }) - }) } @@ -1472,6 +1365,13 @@ func validatorTester(cfg testConfiguration) testResult { result := testResult{} + lastMvxBatchID := uint64(0) + for key := range cfg.amountsOnMvxPendingBatches { + if key > lastMvxBatchID { + lastMvxBatchID = key + } + } + args.MultiversXClient = &bridge.MultiversXClientStub{ CheckRequiredBalanceCalled: func(ctx context.Context, token []byte, value *big.Int) error { result.checkRequiredBalanceOnMvxCalled = true @@ -1554,6 +1454,14 @@ func validatorTester(cfg testConfiguration) testResult { return cfg.lastExecutedEthBatch, nil }, + GetLastMvxBatchIDCalled: func(ctx context.Context) (uint64, error) { + err := cfg.errorsOnCalls["GetLastMvxBatchID"] + if err != nil { + return 0, err + } + + return lastMvxBatchID, nil + }, } args.EthereumClient = &bridge.EthereumClientStub{ CheckRequiredBalanceCalled: func(ctx context.Context, erc20Address common.Address, value *big.Int) error { @@ -1613,12 +1521,14 @@ func validatorTester(cfg testConfiguration) testResult { return batch, false, nil }, - GetTransactionsStatusesCalled: func(ctx context.Context, batchId uint64) ([]byte, error) { - if cfg.isFinalOnEth { - return make([]byte, 0), nil + WasExecutedCalled: func(ctx context.Context, batchID uint64) (bool, error) { + err := cfg.errorsOnCalls["WasExecutedEth"] + if err != nil { + return false, err } - return nil, errors.New("not a final batch") + _, found := cfg.amountsOnMvxPendingBatches[batchID] + return !found, nil }, } diff --git a/clients/balanceValidator/interface.go b/clients/balanceValidator/interface.go index 272e19d5..7e8c0388 100644 --- a/clients/balanceValidator/interface.go +++ b/clients/balanceValidator/interface.go @@ -13,6 +13,7 @@ type MultiversXClient interface { GetPendingBatch(ctx context.Context) (*bridgeCore.TransferBatch, error) GetBatch(ctx context.Context, batchID uint64) (*bridgeCore.TransferBatch, error) GetLastExecutedEthBatchID(ctx context.Context) (uint64, error) + GetLastMvxBatchID(ctx context.Context) (uint64, error) IsMintBurnToken(ctx context.Context, token []byte) (bool, error) IsNativeToken(ctx context.Context, token []byte) (bool, error) TotalBalances(ctx context.Context, token []byte) (*big.Int, error) @@ -31,6 +32,6 @@ type EthereumClient interface { MintBurnTokens(ctx context.Context, token common.Address) (bool, error) NativeTokens(ctx context.Context, token common.Address) (bool, error) CheckRequiredBalance(ctx context.Context, erc20Address common.Address, value *big.Int) error - GetTransactionsStatuses(ctx context.Context, batchId uint64) ([]byte, error) + WasExecuted(ctx context.Context, mvxBatchID uint64) (bool, error) IsInterfaceNil() bool } diff --git a/clients/ethereum/client.go b/clients/ethereum/client.go index 2268b42d..fc45358f 100644 --- a/clients/ethereum/client.go +++ b/clients/ethereum/client.go @@ -239,9 +239,9 @@ func (c *client) GetBatchSCMetadata(ctx context.Context, nonce uint64, blockNumb return depositEvents, nil } -// WasExecuted returns true if the batch ID was executed -func (c *client) WasExecuted(ctx context.Context, batchID uint64) (bool, error) { - return c.clientWrapper.WasBatchExecuted(ctx, big.NewInt(0).SetUint64(batchID)) +// WasExecuted returns true if the MultiversX batch ID was executed +func (c *client) WasExecuted(ctx context.Context, mvxBatchID uint64) (bool, error) { + return c.clientWrapper.WasBatchExecuted(ctx, big.NewInt(0).SetUint64(mvxBatchID)) } // BroadcastSignatureForMessageHash will send the signature for the provided message hash diff --git a/clients/multiversx/mxClientDataGetter.go b/clients/multiversx/mxClientDataGetter.go index 324f03ea..d1f78491 100644 --- a/clients/multiversx/mxClientDataGetter.go +++ b/clients/multiversx/mxClientDataGetter.go @@ -42,6 +42,7 @@ const ( getMintBalances = "getMintBalances" getBurnBalances = "getBurnBalances" getAllKnownTokens = "getAllKnownTokens" + getLastBatchId = "getLastBatchId" ) // ArgsMXClientDataGetter is the arguments DTO used in the NewMXClientDataGetter constructor @@ -535,6 +536,14 @@ func (dataGetter *mxClientDataGetter) GetAllKnownTokens(ctx context.Context) ([] return dataGetter.executeQueryFromBuilder(ctx, builder) } +// GetLastMvxBatchID returns the highest batch ID the safe contract reached. This might be a WIP batch that is not executable yet +func (dataGetter *mxClientDataGetter) GetLastMvxBatchID(ctx context.Context) (uint64, error) { + builder := dataGetter.createSafeDefaultVmQueryBuilder() + builder.Function(getLastBatchId) + + return dataGetter.executeQueryUint64FromBuilder(ctx, builder) +} + // IsInterfaceNil returns true if there is no value under the interface func (dataGetter *mxClientDataGetter) IsInterfaceNil() bool { return dataGetter == nil diff --git a/clients/multiversx/mxClientDataGetter_test.go b/clients/multiversx/mxClientDataGetter_test.go index d21ea26b..2531acd2 100644 --- a/clients/multiversx/mxClientDataGetter_test.go +++ b/clients/multiversx/mxClientDataGetter_test.go @@ -1588,3 +1588,36 @@ func TestMultiversXClientDataGetter_getBurnBalances(t *testing.T) { assert.Equal(t, result, expectedAccumulatedBurnedTokens) assert.True(t, proxyCalled) } + +func TestMultiversXClientDataGetter_GetLastMvxBatchID(t *testing.T) { + t.Parallel() + + args := createMockArgsMXClientDataGetter() + proxyCalled := false + args.Proxy = &interactors.ProxyStub{ + ExecuteVMQueryCalled: func(ctx context.Context, vmRequest *data.VmValueRequest) (*data.VmValuesResponseData, error) { + proxyCalled = true + assert.Equal(t, getBech32Address(args.SafeContractAddress), vmRequest.Address) + assert.Equal(t, getBech32Address(args.RelayerAddress), vmRequest.CallerAddr) + assert.Equal(t, "", vmRequest.CallValue) + assert.Equal(t, getLastBatchId, vmRequest.FuncName) + assert.Empty(t, vmRequest.Args) + + strResponse := "Dpk=" + response, _ := base64.StdEncoding.DecodeString(strResponse) + return &data.VmValuesResponseData{ + Data: &vm.VMOutputApi{ + ReturnCode: okCodeAfterExecution, + ReturnData: [][]byte{response}, + }, + }, nil + }, + } + + dg, _ := NewMXClientDataGetter(args) + + result, err := dg.GetLastMvxBatchID(context.Background()) + assert.Nil(t, err) + assert.Equal(t, uint64(3737), result) + assert.True(t, proxyCalled) +} diff --git a/integrationTests/mock/mockWriter.go b/integrationTests/mock/mockWriter.go index 50ac35ee..5db37ca7 100644 --- a/integrationTests/mock/mockWriter.go +++ b/integrationTests/mock/mockWriter.go @@ -4,26 +4,44 @@ import "strings" type mockLogObserver struct { expectedStringInLog string + exceptStrings []string logFoundChan chan struct{} } // NewMockLogObserver returns a new instance of mockLogObserver -func NewMockLogObserver(expectedStringInLog string) *mockLogObserver { +func NewMockLogObserver(expectedStringInLog string, exceptStrings ...string) *mockLogObserver { return &mockLogObserver{ expectedStringInLog: expectedStringInLog, + exceptStrings: exceptStrings, logFoundChan: make(chan struct{}, 1), } } // Write is called by the logger func (observer *mockLogObserver) Write(log []byte) (n int, err error) { - if strings.Contains(string(log), observer.expectedStringInLog) { - observer.logFoundChan <- struct{}{} + str := string(log) + if !strings.Contains(str, observer.expectedStringInLog) { + return 0, nil } + if observer.stringIsExcepted(str) { + return 0, nil + } + + observer.logFoundChan <- struct{}{} return 0, nil } +func (observer *mockLogObserver) stringIsExcepted(str string) bool { + for _, exceptString := range observer.exceptStrings { + if strings.Contains(str, exceptString) { + return true + } + } + + return false +} + // LogFoundChan returns the internal chan func (observer *mockLogObserver) LogFoundChan() chan struct{} { return observer.logFoundChan diff --git a/integrationTests/mock/multiversXContractStateMock.go b/integrationTests/mock/multiversXContractStateMock.go index b9502856..8326b5dd 100644 --- a/integrationTests/mock/multiversXContractStateMock.go +++ b/integrationTests/mock/multiversXContractStateMock.go @@ -270,6 +270,8 @@ func (mock *multiversXContractStateMock) processVmRequests(vmRequest *data.VmVal return mock.vmRequestGetMintBalances(vmRequest), nil case "getBurnBalances": return mock.vmRequestGetBurnBalances(vmRequest), nil + case "getLastBatchId": + return mock.vmRequestGetLastBatchId(vmRequest), nil } panic("unimplemented function: " + vmRequest.FuncName) @@ -330,7 +332,7 @@ func (mock *multiversXContractStateMock) vmRequestGetStatusesAfterExecution(_ *d } func (mock *multiversXContractStateMock) sign(dataSplit []string, tx *transaction.FrontendTransaction) { - actionID := getActionIDFromString(dataSplit[1]) + actionID := getBigIntFromString(dataSplit[1]) if !mock.actionIDExists(actionID) { panic(fmt.Sprintf("attempted to sign on a missing action ID: %v as big int, raw: %s", actionID, dataSplit[1])) } @@ -344,7 +346,7 @@ func (mock *multiversXContractStateMock) sign(dataSplit []string, tx *transactio } func (mock *multiversXContractStateMock) performAction(dataSplit []string, _ *transaction.FrontendTransaction) { - actionID := getActionIDFromString(dataSplit[1]) + actionID := getBigIntFromString(dataSplit[1]) if !mock.actionIDExists(actionID) { panic(fmt.Sprintf("attempted to perform on a missing action ID: %v as big int, raw: %s", actionID, dataSplit[1])) } @@ -360,7 +362,7 @@ func (mock *multiversXContractStateMock) performAction(dataSplit []string, _ *tr } func (mock *multiversXContractStateMock) vmRequestWasActionExecuted(vmRequest *data.VmValueRequest) *data.VmValuesResponseData { - actionID := getActionIDFromString(vmRequest.Args[0]) + actionID := getBigIntFromString(vmRequest.Args[0]) if mock.performedAction == nil { return createOkVmResponse([][]byte{BoolToByteSlice(false)}) @@ -390,7 +392,7 @@ func (mock *multiversXContractStateMock) actionIDExists(actionID *big.Int) bool } func (mock *multiversXContractStateMock) vmRequestQuorumReached(vmRequest *data.VmValueRequest) *data.VmValuesResponseData { - actionID := getActionIDFromString(vmRequest.Args[0]) + actionID := getBigIntFromString(vmRequest.Args[0]) m, found := mock.signedActionIDs[actionID.String()] if !found { return createOkVmResponse([][]byte{BoolToByteSlice(false)}) @@ -434,6 +436,10 @@ func (mock *multiversXContractStateMock) vmRequestGetCurrentPendingBatch(_ *data return createOkVmResponse(make([][]byte, 0)) } + return mock.responseWithPendingBatch() +} + +func (mock *multiversXContractStateMock) responseWithPendingBatch() *data.VmValuesResponseData { args := [][]byte{mock.pendingBatch.Nonce.Bytes()} // first non-empty slice for _, deposit := range mock.pendingBatch.MultiversXDeposits { args = append(args, make([]byte, 0)) // mocked block nonce @@ -446,7 +452,16 @@ func (mock *multiversXContractStateMock) vmRequestGetCurrentPendingBatch(_ *data return createOkVmResponse(args) } -func (mock *multiversXContractStateMock) vmRequestGetBatch(_ *data.VmValueRequest) *data.VmValuesResponseData { +func (mock *multiversXContractStateMock) vmRequestGetBatch(request *data.VmValueRequest) *data.VmValuesResponseData { + if mock.pendingBatch == nil { + return createOkVmResponse(make([][]byte, 0)) + } + + nonce := getBigIntFromString(request.Args[0]) + if nonce.Cmp(mock.pendingBatch.Nonce) == 0 { + return mock.responseWithPendingBatch() + } + return createOkVmResponse(make([][]byte, 0)) } @@ -456,7 +471,7 @@ func (mock *multiversXContractStateMock) setPendingBatch(pendingBatch *Multivers func (mock *multiversXContractStateMock) vmRequestSigned(request *data.VmValueRequest) *data.VmValuesResponseData { hexAddress := request.Args[0] - actionID := getActionIDFromString(request.Args[1]) + actionID := getBigIntFromString(request.Args[1]) actionIDMap, found := mock.signedActionIDs[actionID.String()] if !found { @@ -512,7 +527,14 @@ func (mock *multiversXContractStateMock) vmRequestGetBurnBalances(vmRequest *dat return createOkVmResponse([][]byte{mock.getBurnBalances(address).Bytes()}) } -func getActionIDFromString(data string) *big.Int { +func (mock *multiversXContractStateMock) vmRequestGetLastBatchId(_ *data.VmValueRequest) *data.VmValuesResponseData { + if mock.pendingBatch == nil { + return createOkVmResponse([][]byte{big.NewInt(0).Bytes()}) + } + return createOkVmResponse([][]byte{mock.pendingBatch.Nonce.Bytes()}) +} + +func getBigIntFromString(data string) *big.Int { buff, err := hex.DecodeString(data) if err != nil { panic(err) diff --git a/integrationTests/relayers/slowTests/edgeCases_test.go b/integrationTests/relayers/slowTests/edgeCases_test.go new file mode 100644 index 00000000..e4a6d68e --- /dev/null +++ b/integrationTests/relayers/slowTests/edgeCases_test.go @@ -0,0 +1,95 @@ +//go:build slow + +package slowTests + +import ( + "context" + "errors" + "math/big" + "testing" + + "github.com/multiversx/mx-bridge-eth-go/integrationTests/mock" + "github.com/multiversx/mx-bridge-eth-go/integrationTests/relayers/slowTests/framework" + logger "github.com/multiversx/mx-chain-logger-go" + "github.com/stretchr/testify/require" +) + +func TestRelayerShouldExecuteSimultaneousSwapsAndNotCatchErrors(t *testing.T) { + errorString := "ERROR" + mockLogObserver := mock.NewMockLogObserver(errorString, "got invalid action ID") + err := logger.AddLogObserver(mockLogObserver, &logger.PlainFormatter{}) + require.NoError(t, err) + defer func() { + require.NoError(t, logger.RemoveLogObserver(mockLogObserver)) + }() + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + stopChan := make(chan error, 1000) // ensure sufficient error buffer + + go func() { + for { + select { + case <-ctx.Done(): + return + case <-mockLogObserver.LogFoundChan(): + stopChan <- errors.New("logger should have not caught errors") + } + } + }() + + usdcToken := GenerateTestUSDCToken() + usdcToken.TestOperations = []framework.TokenOperations{ + { + ValueToTransferToMvx: big.NewInt(5000), + ValueToSendFromMvX: big.NewInt(200), + MvxSCCallData: nil, + MvxFaultySCCall: false, + MvxForceSCCall: false, + }, + } + usdcToken.ESDTSafeExtraBalance = big.NewInt(50) + usdcToken.EthTestAddrExtraBalance = big.NewInt(-5000 - 5000 + 200 - 50) + + _ = testRelayersWithChainSimulatorAndTokensForSimultaneousSwaps( + t, + stopChan, + usdcToken, + ) +} + +func testRelayersWithChainSimulatorAndTokensForSimultaneousSwaps(tb testing.TB, manualStopChan chan error, tokens ...framework.TestTokenParams) *framework.TestSetup { + startsFromEthFlow := &startsFromEthereumEdgecaseFlow{ + TB: tb, + tokens: tokens, + } + + setupFunc := func(tb testing.TB, setup *framework.TestSetup) { + startsFromEthFlow.setup = setup + + setup.IssueAndConfigureTokens(tokens...) + setup.MultiversxHandler.CheckForZeroBalanceOnReceivers(setup.Ctx, tokens...) + setup.EthereumHandler.CreateBatchOnEthereum(setup.Ctx, setup.MultiversxHandler.TestCallerAddress, startsFromEthFlow.tokens...) + } + + processFunc := func(tb testing.TB, setup *framework.TestSetup) bool { + if startsFromEthFlow.process() { + setup.TestWithdrawTotalFeesOnEthereumForTokens(startsFromEthFlow.tokens...) + + return true + } + + setup.EthereumHandler.SimulatedChain.Commit() + setup.ChainSimulator.GenerateBlocks(setup.Ctx, 1) + require.LessOrEqual(tb, setup.ScCallerModuleInstance.GetNumSentTransaction(), setup.GetNumScCallsOperations()) + + return false + } + + return testRelayersWithChainSimulator(tb, + setupFunc, + processFunc, + manualStopChan, + ) +} diff --git a/integrationTests/relayers/slowTests/startsFromEthereumEdgecaseFlow.go b/integrationTests/relayers/slowTests/startsFromEthereumEdgecaseFlow.go new file mode 100644 index 00000000..214581af --- /dev/null +++ b/integrationTests/relayers/slowTests/startsFromEthereumEdgecaseFlow.go @@ -0,0 +1,49 @@ +//go:build slow + +package slowTests + +import ( + "fmt" + "testing" + + "github.com/multiversx/mx-bridge-eth-go/integrationTests/relayers/slowTests/framework" +) + +type startsFromEthereumEdgecaseFlow struct { + testing.TB + setup *framework.TestSetup + ethToMvxDone bool + mvxToEthDone bool + tokens []framework.TestTokenParams +} + +func (flow *startsFromEthereumEdgecaseFlow) process() (finished bool) { + if len(flow.tokens) == 0 { + return true + } + if flow.mvxToEthDone && flow.ethToMvxDone { + return true + } + + isTransferDoneFromEthereum := flow.setup.IsTransferDoneFromEthereum(flow.tokens...) + if !flow.ethToMvxDone && isTransferDoneFromEthereum { + flow.ethToMvxDone = true + log.Info(fmt.Sprintf(framework.LogStepMarker, "Ethereum->MultiversX transfer finished, now sending back to Ethereum & another round from Ethereum...")) + + flow.setup.SendFromMultiversxToEthereum(flow.tokens...) + flow.setup.EthereumHandler.SendFromEthereumToMultiversX(flow.setup.Ctx, flow.setup.MultiversxHandler.TestCallerAddress, flow.tokens...) + } + if !flow.ethToMvxDone { + // return here, no reason to check downwards + return false + } + + isTransferDoneFromMultiversX := flow.setup.IsTransferDoneFromMultiversX(flow.tokens...) + if !flow.mvxToEthDone && isTransferDoneFromMultiversX { + flow.mvxToEthDone = true + log.Info(fmt.Sprintf(framework.LogStepMarker, "MultiversX<->Ethereum from Ethereum transfers done")) + return true + } + + return false +} diff --git a/testsCommon/bridge/multiversxClientStub.go b/testsCommon/bridge/multiversxClientStub.go index fd115f6a..aeba233c 100644 --- a/testsCommon/bridge/multiversxClientStub.go +++ b/testsCommon/bridge/multiversxClientStub.go @@ -38,6 +38,7 @@ type MultiversXClientStub struct { MintBalancesCalled func(ctx context.Context, token []byte) (*big.Int, error) BurnBalancesCalled func(ctx context.Context, token []byte) (*big.Int, error) CheckRequiredBalanceCalled func(ctx context.Context, token []byte, value *big.Int) error + GetLastMvxBatchIDCalled func(ctx context.Context) (uint64, error) CloseCalled func() error } @@ -260,6 +261,15 @@ func (stub *MultiversXClientStub) CheckRequiredBalance(ctx context.Context, toke return nil } +// GetLastMvxBatchID - +func (stub *MultiversXClientStub) GetLastMvxBatchID(ctx context.Context) (uint64, error) { + if stub.GetLastMvxBatchIDCalled != nil { + return stub.GetLastMvxBatchIDCalled(ctx) + } + + return 0, nil +} + // Close - func (stub *MultiversXClientStub) Close() error { if stub.CloseCalled != nil {