Skip to content

Commit

Permalink
E2E: Concurrent withdrawals [WIP]
Browse files Browse the repository at this point in the history
  • Loading branch information
swift1337 committed Oct 31, 2024
1 parent 1a9d185 commit b72b8d9
Show file tree
Hide file tree
Showing 7 changed files with 119 additions and 18 deletions.
8 changes: 5 additions & 3 deletions cmd/zetae2e/local/local.go
Original file line number Diff line number Diff line change
Expand Up @@ -416,9 +416,11 @@ func localE2ETest(cmd *cobra.Command, _ []string) {
}

tonTests := []string{
e2etests.TestTONDepositName,
e2etests.TestTONDepositAndCallName,
e2etests.TestTONWithdrawName,
// todo
//e2etests.TestTONDepositName,
//e2etests.TestTONDepositAndCallName,
//e2etests.TestTONWithdrawName,
e2etests.TestTONWithdrawConcurrentName,
}

eg.Go(tonTestRoutine(conf, deployerRunner, verbose, tonTests...))
Expand Down
13 changes: 10 additions & 3 deletions e2e/e2etests/e2etests.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,10 @@ const (
/**
* TON tests
*/
TestTONDepositName = "ton_deposit"
TestTONDepositAndCallName = "ton_deposit_and_call"
TestTONWithdrawName = "ton_withdraw"
TestTONDepositName = "ton_deposit"
TestTONDepositAndCallName = "ton_deposit_and_call"
TestTONWithdrawName = "ton_withdraw"
TestTONWithdrawConcurrentName = "ton_withdraw_concurrent"

/*
Bitcoin tests
Expand Down Expand Up @@ -480,6 +481,12 @@ var AllE2ETests = []runner.E2ETest{
},
TestTONWithdraw,
),
runner.NewE2ETest(
TestTONWithdrawConcurrentName,
"withdraw TON from ZEVM for several recipients simultaneously",
[]runner.ArgDefinition{},
TestTONWithdrawConcurrent,
),
/*
Bitcoin tests
*/
Expand Down
7 changes: 2 additions & 5 deletions e2e/e2etests/test_ton_withdrawal.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,6 @@ import (
"github.com/zeta-chain/node/zetaclient/chains/ton/liteapi"
)

// TODO: Add "withdraw_many_concurrent" test
// https://github.com/zeta-chain/node/issues/3044

func TestTONWithdraw(r *runner.E2ERunner, args []string) {
// ARRANGE
require.Len(r, args, 1)
Expand All @@ -34,7 +31,7 @@ func TestTONWithdraw(r *runner.E2ERunner, args []string) {
tonRecipient, err := deployer.CreateWallet(r.Ctx, toncontracts.Coins(1))
require.NoError(r, err)

tonRecipientBalanceBefore, err := deployer.GetBalanceOf(r.Ctx, tonRecipient.GetAddress())
tonRecipientBalanceBefore, err := deployer.GetBalanceOf(r.Ctx, tonRecipient.GetAddress(), true)
require.NoError(r, err)

r.Logger.Info("Recipient's TON balance before withdrawal: %s", toncontracts.FormatCoins(tonRecipientBalanceBefore))
Expand All @@ -61,7 +58,7 @@ func TestTONWithdraw(r *runner.E2ERunner, args []string) {
)

// Make sure that recipient's TON balance has increased
tonRecipientBalanceAfter, err := deployer.GetBalanceOf(r.Ctx, tonRecipient.GetAddress())
tonRecipientBalanceAfter, err := deployer.GetBalanceOf(r.Ctx, tonRecipient.GetAddress(), true)
require.NoError(r, err)

r.Logger.Info("Recipient's balance after withdrawal: %s", toncontracts.FormatCoins(tonRecipientBalanceAfter))
Expand Down
80 changes: 80 additions & 0 deletions e2e/e2etests/test_ton_withdrawal_concurrent.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package e2etests

import (
"math/rand"
"sync"

"cosmossdk.io/math"
ethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/stretchr/testify/require"
"github.com/tonkeeper/tongo/ton"

"github.com/zeta-chain/node/e2e/runner"
"github.com/zeta-chain/node/e2e/utils"
toncontracts "github.com/zeta-chain/node/pkg/contracts/ton"
"github.com/zeta-chain/node/testutil/sample"
cc "github.com/zeta-chain/node/x/crosschain/types"
)

func TestTONWithdrawConcurrent(r *runner.E2ERunner, _ []string) {
// ARRANGE
// Given a deployer
_, deployer := r.Ctx, r.TONDeployer

const recipientsCount = 10
type withdrawal struct {
recipient ton.AccountID
amount math.Uint
}

var (
testCases []withdrawal
wg sync.WaitGroup
)

// Given multiple recipients WITHOUT deployed wallet-contracts
// and sample withdrawal amounts between 1 and 5 TON
for i := 0; i < recipientsCount; i++ {
// #nosec G404: it's a test
amount := 1 + rand.Intn(5)
testCases = append(testCases, withdrawal{
// #nosec G115 test - always in range
amount: toncontracts.Coins(uint64(amount)),
recipient: sample.GenerateTONAccountID(),
})
}

// ACT
// Fire withdrawals. Note that zevm sender is r.ZEVMAuth
for i, tc := range testCases {
r.Logger.Info(
"Withdrawal #%d: sending %s to %s",
i+1,
toncontracts.FormatCoins(tc.amount),
tc.recipient.ToRaw(),
)

approvedAmount := tc.amount.Add(toncontracts.Coins(1))
tx := r.SendWithdrawTONZRC20(tc.recipient, tc.amount.BigInt(), approvedAmount.BigInt())

wg.Add(1)

go func(number int, tx *ethtypes.Transaction) {
defer wg.Done()

// wait for the cctx to be mined
cctx := utils.WaitCctxMinedByInboundHash(r.Ctx, tx.Hash().Hex(), r.CctxClient, r.Logger, r.CctxTimeout)

// ASSERT
utils.RequireCCTXStatus(r, cctx, cc.CctxStatus_OutboundMined)
r.Logger.Info("Withdrawal #%d complete! cctx index: %s", number, cctx.Index)

// Check recipient's balance ON TON
balance, err := deployer.GetBalanceOf(r.Ctx, tc.recipient, false)
require.NoError(r, err, "failed to get balance of %s", tc.recipient.ToRaw())
require.Equal(r, tc.amount.Uint64(), balance.Uint64())
}(i+1, tx)
}

wg.Wait()
}
2 changes: 1 addition & 1 deletion e2e/runner/setup_ton.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func (r *E2ERunner) SetupTON() error {
)

// 3. Check that the gateway indeed was deployed and has desired TON balance.
gwBalance, err := deployer.GetBalanceOf(ctx, gwAccount.ID)
gwBalance, err := deployer.GetBalanceOf(ctx, gwAccount.ID, true)
if err != nil {
return errors.Wrap(err, "unable to get balance of TON gateway")
}
Expand Down
16 changes: 14 additions & 2 deletions e2e/runner/ton.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

"cosmossdk.io/math"
eth "github.com/ethereum/go-ethereum/common"
ethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/pkg/errors"
"github.com/stretchr/testify/require"
"github.com/tonkeeper/tongo/ton"
Expand Down Expand Up @@ -101,8 +102,12 @@ func (r *E2ERunner) TONDepositAndCall(
return cctx, nil
}

// WithdrawTONZRC20 withdraws an amount of ZRC20 TON tokens
func (r *E2ERunner) WithdrawTONZRC20(to ton.AccountID, amount *big.Int, approveAmount *big.Int) *cctypes.CrossChainTx {
// SendWithdrawTONZRC20 sends withdraw tx of TON ZRC20 tokens
func (r *E2ERunner) SendWithdrawTONZRC20(
to ton.AccountID,
amount *big.Int,
approveAmount *big.Int,
) *ethtypes.Transaction {
// approve
tx, err := r.TONZRC20.Approve(r.ZEVMAuth, r.TONZRC20Addr, approveAmount)
require.NoError(r, err)
Expand All @@ -119,6 +124,13 @@ func (r *E2ERunner) WithdrawTONZRC20(to ton.AccountID, amount *big.Int, approveA
utils.RequireTxSuccessful(r, receipt, "withdraw")
r.Logger.Info("Receipt txhash %s status %d", receipt.TxHash, receipt.Status)

return tx
}

// WithdrawTONZRC20 withdraws an amount of ZRC20 TON tokens and waits for the cctx to be mined
func (r *E2ERunner) WithdrawTONZRC20(to ton.AccountID, amount *big.Int, approveAmount *big.Int) *cctypes.CrossChainTx {
tx := r.SendWithdrawTONZRC20(to, amount, approveAmount)

// wait for the cctx to be mined
cctx := utils.WaitCctxMinedByInboundHash(r.Ctx, tx.Hash().Hex(), r.CctxClient, r.Logger, r.CctxTimeout)
utils.RequireCCTXStatus(r, cctx, cctypes.CctxStatus_OutboundMined)
Expand Down
11 changes: 7 additions & 4 deletions e2e/runner/ton/deployer.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,13 @@ func (d *Deployer) Seqno(ctx context.Context) (uint32, error) {
return d.blockchain.GetSeqno(ctx, d.GetAddress())
}

// GetBalanceOf returns the balance of the given account.
func (d *Deployer) GetBalanceOf(ctx context.Context, id ton.AccountID) (math.Uint, error) {
if err := d.waitForAccountActivation(ctx, id); err != nil {
return math.Uint{}, errors.Wrap(err, "failed to wait for account activation")
// GetBalanceOf returns the balance of a given account.
// wait=true waits for account activation.
func (d *Deployer) GetBalanceOf(ctx context.Context, id ton.AccountID, wait bool) (math.Uint, error) {
if wait {
if err := d.waitForAccountActivation(ctx, id); err != nil {
return math.Uint{}, errors.Wrap(err, "failed to wait for account activation")
}
}

state, err := d.blockchain.GetAccountState(ctx, id)
Expand Down

0 comments on commit b72b8d9

Please sign in to comment.