Skip to content

Commit

Permalink
split withdrawal stress tests into separate tests
Browse files Browse the repository at this point in the history
  • Loading branch information
skosito committed Dec 11, 2024
1 parent 3fbe758 commit d4cacb6
Show file tree
Hide file tree
Showing 4 changed files with 170 additions and 54 deletions.
4 changes: 2 additions & 2 deletions cmd/zetae2e/local/local.go
Original file line number Diff line number Diff line change
Expand Up @@ -383,8 +383,8 @@ func localE2ETest(cmd *cobra.Command, _ []string) {
if testPerformance {
// eg.Go(ethereumDepositPerformanceRoutine(conf, deployerRunner, verbose, e2etests.TestStressEtherDepositName))
// eg.Go(ethereumWithdrawPerformanceRoutine(conf, deployerRunner, verbose, e2etests.TestStressEtherWithdrawName))
eg.Go(solanaDepositPerformanceRoutine(conf, deployerRunner, verbose, e2etests.TestStressSolanaDepositName))
eg.Go(solanaWithdrawPerformanceRoutine(conf, deployerRunner, verbose, e2etests.TestStressSolanaWithdrawName))
// eg.Go(solanaDepositPerformanceRoutine(conf, deployerRunner, verbose, e2etests.TestStressSolanaDepositName))
eg.Go(solanaWithdrawPerformanceRoutine(conf, deployerRunner, verbose, e2etests.TestStressSolanaWithdrawName, e2etests.TestStressSPLWithdrawName))
}

if testSolana {
Expand Down
16 changes: 12 additions & 4 deletions e2e/e2etests/e2etests.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ const (
TestStressBTCDepositName = "stress_btc_deposit"
TestStressSolanaDepositName = "stress_solana_deposit"
TestStressSolanaWithdrawName = "stress_solana_withdraw"
TestStressSPLWithdrawName = "stress_spl_withdraw"

/*
Admin tests
Expand Down Expand Up @@ -782,15 +783,22 @@ var AllE2ETests = []runner.E2ETest{
),
runner.NewE2ETest(
TestStressSolanaWithdrawName,
"stress test SOL/SPL withdrawals",
"stress test SOL withdrawals",
[]runner.ArgDefinition{
{Description: "amount in lamports", DefaultValue: "1000000"},
{Description: "count of SOL withdrawals", DefaultValue: "10"},
{Description: "amount in SPL tokens", DefaultValue: "1000000"},
{Description: "count of SPL withdrawals", DefaultValue: "10"},
{Description: "count of SOL withdrawals", DefaultValue: "50"},
},
TestStressSolanaWithdraw,
),
runner.NewE2ETest(
TestStressSPLWithdrawName,
"stress test SPL withdrawals",
[]runner.ArgDefinition{
{Description: "amount in SPL tokens", DefaultValue: "1000000"},
{Description: "count of SPL withdrawals", DefaultValue: "50"},
},
TestStressSPLWithdraw,
),
/*
Admin tests
*/
Expand Down
90 changes: 42 additions & 48 deletions e2e/e2etests/test_stress_solana_withdraw.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ package e2etests
import (
"fmt"
"math/big"
"sync"
"time"

"github.com/ethereum/go-ethereum/accounts/abi/bind"
ethcommon "github.com/ethereum/go-ethereum/common"
"github.com/montanaflynn/stats"
"github.com/stretchr/testify/require"
"golang.org/x/sync/errgroup"

Expand All @@ -17,12 +18,10 @@ import (

// TestStressSolanaWithdraw tests the stressing withdrawal of SOL/SPL
func TestStressSolanaWithdraw(r *runner.E2ERunner, args []string) {
require.Len(r, args, 4)
require.Len(r, args, 2)

withdrawSOLAmount := utils.ParseBigInt(r, args[0])
numWithdrawalsSOL := utils.ParseInt(r, args[1])
withdrawSPLAmount := utils.ParseBigInt(r, args[2])
numWithdrawalsSPL := utils.ParseInt(r, args[3])

balanceBefore, err := r.SOLZRC20.BalanceOf(&bind.CallOpts{}, r.EVMAddress())
require.NoError(r, err)
Expand All @@ -35,26 +34,20 @@ func TestStressSolanaWithdraw(r *runner.E2ERunner, args []string) {
// load deployer private key
privKey := r.GetSolanaPrivKey()

r.Logger.Print("starting stress test of %d SOL and %d SPL withdrawals", numWithdrawalsSOL, numWithdrawalsSPL)
r.Logger.Print("starting stress test of %d SOL withdrawals", numWithdrawalsSOL)

tx, err := r.SOLZRC20.Approve(r.ZEVMAuth, r.SOLZRC20Addr, big.NewInt(1e18))
require.NoError(r, err)
receipt := utils.MustWaitForTxReceipt(r.Ctx, r.ZEVMClient, tx, r.Logger, r.ReceiptTimeout)
utils.RequireTxSuccessful(r, receipt, "approve_sol")

tx, err = r.SPLZRC20.Approve(r.ZEVMAuth, r.SPLZRC20Addr, big.NewInt(1e18))
require.NoError(r, err)
receipt = utils.MustWaitForTxReceipt(r.Ctx, r.ZEVMClient, tx, r.Logger, r.ReceiptTimeout)
utils.RequireTxSuccessful(r, receipt, "approve_spl")

tx, err = r.SOLZRC20.Approve(r.ZEVMAuth, r.SPLZRC20Addr, big.NewInt(1e18))
require.NoError(r, err)
receipt = utils.MustWaitForTxReceipt(r.Ctx, r.ZEVMClient, tx, r.Logger, r.ReceiptTimeout)
utils.RequireTxSuccessful(r, receipt, "approve_spl_sol")

// create a wait group to wait for all the withdrawals to complete
var eg errgroup.Group

// store durations as float64 seconds like prometheus
withdrawDurations := []float64{}
withdrawDurationsLock := sync.Mutex{}

// send the withdrawals SOL
for i := 0; i < numWithdrawalsSOL; i++ {
i := i
Expand All @@ -68,44 +61,45 @@ func TestStressSolanaWithdraw(r *runner.E2ERunner, args []string) {

r.Logger.Print("index %d: starting SOL withdraw, tx hash: %s", i, tx.Hash().Hex())

eg.Go(func() error { return monitorWithdrawal(r, tx.Hash(), i, time.Now()) })
eg.Go(func() error {
startTime := time.Now()
cctx := utils.WaitCctxMinedByInboundHash(r.Ctx, tx.Hash().Hex(), r.CctxClient, r.Logger, r.ReceiptTimeout)
if cctx.CctxStatus.Status != crosschaintypes.CctxStatus_OutboundMined {
return fmt.Errorf(
"index %d: withdraw cctx failed with status %s, message %s, cctx index %s",
i,
cctx.CctxStatus.Status,
cctx.CctxStatus.StatusMessage,
cctx.Index,
)
}
timeToComplete := time.Since(startTime)
r.Logger.Print("index %d: withdraw cctx success in %s", i, timeToComplete.String())

withdrawDurationsLock.Lock()
withdrawDurations = append(withdrawDurations, timeToComplete.Seconds())
withdrawDurationsLock.Unlock()

return nil
})
}

// send the withdrawals SPL
for i := 0; i < numWithdrawalsSPL; i++ {
i := i

// execute the withdraw SPL transaction
tx, err = r.SPLZRC20.Withdraw(r.ZEVMAuth, []byte(privKey.PublicKey().String()), withdrawSPLAmount)
require.NoError(r, err)

receipt := utils.MustWaitForTxReceipt(r.Ctx, r.ZEVMClient, tx, r.Logger, r.ReceiptTimeout)
utils.RequireTxSuccessful(r, receipt)

r.Logger.Print("index %d: starting SPL withdraw, tx hash: %s", i, tx.Hash().Hex())
err = eg.Wait()

eg.Go(func() error { return monitorWithdrawal(r, tx.Hash(), i, time.Now()) })
desc, descErr := stats.Describe(withdrawDurations, false, &[]float64{50.0, 75.0, 90.0, 95.0})
if descErr != nil {
r.Logger.Print("❌ failed to calculate latency report: %v", descErr)
}

require.NoError(r, eg.Wait())

r.Logger.Print("all withdrawals completed")
}

// monitorWithdrawal monitors the withdrawal of SOL/SPL, returns once the withdrawal is complete
func monitorWithdrawal(r *runner.E2ERunner, hash ethcommon.Hash, index int, startTime time.Time) error {
cctx := utils.WaitCctxMinedByInboundHash(r.Ctx, hash.String(), r.CctxClient, r.Logger, r.ReceiptTimeout)
if cctx.CctxStatus.Status != crosschaintypes.CctxStatus_OutboundMined {
return fmt.Errorf(
"index %d: withdraw cctx failed with status %s, message %s, cctx index %s",
index,
cctx.CctxStatus.Status,
cctx.CctxStatus.StatusMessage,
cctx.Index,
)
r.Logger.Print("Latency report:")
r.Logger.Print("min: %.2f", desc.Min)
r.Logger.Print("max: %.2f", desc.Max)
r.Logger.Print("mean: %.2f", desc.Mean)
r.Logger.Print("std: %.2f", desc.Std)
for _, p := range desc.DescriptionPercentiles {
r.Logger.Print("p%.0f: %.2f", p.Percentile, p.Value)
}
timeToComplete := time.Since(startTime)
r.Logger.Print("index %d: withdraw cctx success in %s", index, timeToComplete.String())

return nil
require.NoError(r, err)
r.Logger.Print("all SOL withdrawals completed")
}
114 changes: 114 additions & 0 deletions e2e/e2etests/test_stress_spl_withdraw.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package e2etests

import (
"fmt"
"math/big"
"sync"
"time"

"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/montanaflynn/stats"
"github.com/stretchr/testify/require"
"github.com/zeta-chain/node/e2e/runner"
"github.com/zeta-chain/node/e2e/utils"
crosschaintypes "github.com/zeta-chain/node/x/crosschain/types"
"golang.org/x/sync/errgroup"
)

// TestStressSPLWithdraw tests the stressing withdrawal of SOL/SPL
func TestStressSPLWithdraw(r *runner.E2ERunner, args []string) {
require.Len(r, args, 2)

withdrawSPLAmount := utils.ParseBigInt(r, args[0])
numWithdrawalsSPL := utils.ParseInt(r, args[1])

balanceBefore, err := r.SOLZRC20.BalanceOf(&bind.CallOpts{}, r.EVMAddress())
require.NoError(r, err)
r.Logger.Info("runner balance of SOL before withdraw: %s", balanceBefore.String())

balanceBefore, err = r.SPLZRC20.BalanceOf(&bind.CallOpts{}, r.EVMAddress())
require.NoError(r, err)
r.Logger.Info("runner balance of SPL before withdraw: %s", balanceBefore.String())

// load deployer private key
privKey := r.GetSolanaPrivKey()

r.Logger.Print("starting stress test of %d SPL withdrawals", numWithdrawalsSPL)

tx, err := r.SOLZRC20.Approve(r.ZEVMAuth, r.SOLZRC20Addr, big.NewInt(1e18))
require.NoError(r, err)
receipt := utils.MustWaitForTxReceipt(r.Ctx, r.ZEVMClient, tx, r.Logger, r.ReceiptTimeout)
utils.RequireTxSuccessful(r, receipt, "approve_sol")

tx, err = r.SPLZRC20.Approve(r.ZEVMAuth, r.SPLZRC20Addr, big.NewInt(1e18))
require.NoError(r, err)
receipt = utils.MustWaitForTxReceipt(r.Ctx, r.ZEVMClient, tx, r.Logger, r.ReceiptTimeout)
utils.RequireTxSuccessful(r, receipt, "approve_spl")

tx, err = r.SOLZRC20.Approve(r.ZEVMAuth, r.SPLZRC20Addr, big.NewInt(1e18))
require.NoError(r, err)
receipt = utils.MustWaitForTxReceipt(r.Ctx, r.ZEVMClient, tx, r.Logger, r.ReceiptTimeout)
utils.RequireTxSuccessful(r, receipt, "approve_spl_sol")

// create a wait group to wait for all the withdrawals to complete
var eg errgroup.Group

// store durations as float64 seconds like prometheus
withdrawDurations := []float64{}
withdrawDurationsLock := sync.Mutex{}

// send the withdrawals SPL
for i := 0; i < numWithdrawalsSPL; i++ {
i := i

// execute the withdraw SPL transaction
tx, err = r.SPLZRC20.Withdraw(r.ZEVMAuth, []byte(privKey.PublicKey().String()), withdrawSPLAmount)
require.NoError(r, err)

receipt := utils.MustWaitForTxReceipt(r.Ctx, r.ZEVMClient, tx, r.Logger, r.ReceiptTimeout)
utils.RequireTxSuccessful(r, receipt)

r.Logger.Print("index %d: starting SPL withdraw, tx hash: %s", i, tx.Hash().Hex())

eg.Go(func() error {
startTime := time.Now()
cctx := utils.WaitCctxMinedByInboundHash(r.Ctx, tx.Hash().Hex(), r.CctxClient, r.Logger, r.ReceiptTimeout)
if cctx.CctxStatus.Status != crosschaintypes.CctxStatus_OutboundMined {
return fmt.Errorf(
"index %d: withdraw cctx failed with status %s, message %s, cctx index %s",
i,
cctx.CctxStatus.Status,
cctx.CctxStatus.StatusMessage,
cctx.Index,
)
}
timeToComplete := time.Since(startTime)
r.Logger.Print("index %d: withdraw cctx success in %s", i, timeToComplete.String())

withdrawDurationsLock.Lock()
withdrawDurations = append(withdrawDurations, timeToComplete.Seconds())
withdrawDurationsLock.Unlock()

return nil
})
}

err = eg.Wait()

desc, descErr := stats.Describe(withdrawDurations, false, &[]float64{50.0, 75.0, 90.0, 95.0})
if descErr != nil {
r.Logger.Print("❌ failed to calculate latency report: %v", descErr)
}

r.Logger.Print("Latency report:")
r.Logger.Print("min: %.2f", desc.Min)
r.Logger.Print("max: %.2f", desc.Max)
r.Logger.Print("mean: %.2f", desc.Mean)
r.Logger.Print("std: %.2f", desc.Std)
for _, p := range desc.DescriptionPercentiles {
r.Logger.Print("p%.0f: %.2f", p.Percentile, p.Value)
}

require.NoError(r, err)
r.Logger.Print("all SPL withdrawals completed")
}

0 comments on commit d4cacb6

Please sign in to comment.