- Added comprehensive bounds checking to prevent buffer overruns in multicall parsing - Implemented graduated validation system (Strict/Moderate/Permissive) to reduce false positives - Added LRU caching system for address validation with 10-minute TTL - Enhanced ABI decoder with missing Universal Router and Arbitrum-specific DEX signatures - Fixed duplicate function declarations and import conflicts across multiple files - Added error recovery mechanisms with multiple fallback strategies - Updated tests to handle new validation behavior for suspicious addresses - Fixed parser test expectations for improved validation system - Applied gofmt formatting fixes to ensure code style compliance - Fixed mutex copying issues in monitoring package by introducing MetricsSnapshot - Resolved critical security vulnerabilities in heuristic address extraction - Progress: Updated TODO audit from 10% to 35% complete 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
819 lines
25 KiB
Go
819 lines
25 KiB
Go
//go:build stress
|
|
// +build stress
|
|
|
|
package stress_test
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"math/big"
|
|
"math/rand"
|
|
"sync"
|
|
"sync/atomic"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
"github.com/holiman/uint256"
|
|
|
|
"github.com/fraktal/mev-beta/internal/logger"
|
|
"github.com/fraktal/mev-beta/pkg/arbitrum"
|
|
"github.com/fraktal/mev-beta/pkg/events"
|
|
"github.com/fraktal/mev-beta/pkg/pools"
|
|
"github.com/fraktal/mev-beta/pkg/profitcalc"
|
|
"github.com/fraktal/mev-beta/pkg/scanner/market"
|
|
"github.com/fraktal/mev-beta/pkg/trading"
|
|
)
|
|
|
|
// BenchmarkStressTestSuite runs benchmark tests for the stress test suite
|
|
func BenchmarkStressTestSuite(b *testing.B) {
|
|
// Create test logger
|
|
log := logger.New("warn", "text", "") // Use warn level to minimize logging overhead
|
|
|
|
// Create test components
|
|
protocolRegistry := arbitrum.NewArbitrumProtocolRegistry(log)
|
|
poolCache := pools.NewPoolCache(10000, time.Hour)
|
|
marketDiscovery := market.NewMarketDiscovery(nil, log, "")
|
|
strategyEngine := arbitrum.NewMEVStrategyEngine(log, protocolRegistry)
|
|
profitCalculator := profitcalc.NewProfitCalculatorWithClient(log, nil)
|
|
mevAnalyzer := arbitrum.NewMEVAnalyzer(log)
|
|
slippageProtector := trading.NewSlippageProtection(nil, log)
|
|
capitalOptimizer := arbitrum.NewCapitalOptimizer(log)
|
|
profitTracker := arbitrum.NewProfitabilityTracker(log)
|
|
|
|
// Create stress test suite
|
|
suite := NewStressTestSuite(
|
|
log,
|
|
protocolRegistry,
|
|
poolCache,
|
|
marketDiscovery,
|
|
strategyEngine,
|
|
profitCalculator,
|
|
mevAnalyzer,
|
|
slippageProtector,
|
|
capitalOptimizer,
|
|
profitTracker,
|
|
)
|
|
|
|
// Run benchmark tests
|
|
b.Run("MarketScannerStressTest", func(b *testing.B) {
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
result := suite.RunMarketScannerStressTest()
|
|
if !result.Passed {
|
|
b.Fatalf("Market scanner stress test failed: %v", result.Errors)
|
|
}
|
|
}
|
|
})
|
|
|
|
b.Run("SwapAnalyzerStressTest", func(b *testing.B) {
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
result := suite.RunSwapAnalyzerStressTest()
|
|
if !result.Passed {
|
|
b.Fatalf("Swap analyzer stress test failed: %v", result.Errors)
|
|
}
|
|
}
|
|
})
|
|
|
|
b.Run("PoolDiscoveryStressTest", func(b *testing.B) {
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
result := suite.RunPoolDiscoveryStressTest()
|
|
if !result.Passed {
|
|
b.Fatalf("Pool discovery stress test failed: %v", result.Errors)
|
|
}
|
|
}
|
|
})
|
|
|
|
b.Run("ArbitrageEngineStressTest", func(b *testing.B) {
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
result := suite.RunArbitrageEngineStressTest()
|
|
if !result.Passed {
|
|
b.Fatalf("Arbitrage engine stress test failed: %v", result.Errors)
|
|
}
|
|
}
|
|
})
|
|
|
|
b.Run("EventProcessingStressTest", func(b *testing.B) {
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
result := suite.RunEventProcessingStressTest()
|
|
if !result.Passed {
|
|
b.Fatalf("Event processing stress test failed: %v", result.Errors)
|
|
}
|
|
}
|
|
})
|
|
|
|
b.Run("ProfitCalculationStressTest", func(b *testing.B) {
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
result := suite.RunProfitCalculationStressTest()
|
|
if !result.Passed {
|
|
b.Fatalf("Profit calculation stress test failed: %v", result.Errors)
|
|
}
|
|
}
|
|
})
|
|
|
|
b.Run("ConcurrencyStressTest", func(b *testing.B) {
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
result := suite.RunConcurrencyStressTest()
|
|
if !result.Passed {
|
|
b.Fatalf("Concurrency stress test failed: %v", result.Errors)
|
|
}
|
|
}
|
|
})
|
|
|
|
b.Run("MemoryStressTest", func(b *testing.B) {
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
result := suite.RunMemoryStressTest()
|
|
if !result.Passed {
|
|
b.Fatalf("Memory stress test failed: %v", result.Errors)
|
|
}
|
|
}
|
|
})
|
|
|
|
b.Run("PerformanceRegressionTest", func(b *testing.B) {
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
result := suite.RunPerformanceRegressionTest()
|
|
if !result.Passed {
|
|
b.Fatalf("Performance regression test failed: %v", result.Errors)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
// BenchmarkConcurrentMarketScanning benchmarks concurrent market scanning performance
|
|
func BenchmarkConcurrentMarketScanning(b *testing.B) {
|
|
// Create test logger
|
|
log := logger.New("warn", "text", "") // Use warn level to minimize logging overhead
|
|
|
|
// Create test components
|
|
protocolRegistry := arbitrum.NewArbitrumProtocolRegistry(log)
|
|
poolCache := pools.NewPoolCache(10000, time.Hour)
|
|
marketDiscovery := market.NewMarketDiscovery(nil, log, "")
|
|
strategyEngine := arbitrum.NewMEVStrategyEngine(log, protocolRegistry)
|
|
profitCalculator := profitcalc.NewProfitCalculatorWithClient(log, nil)
|
|
mevAnalyzer := arbitrum.NewMEVAnalyzer(log)
|
|
slippageProtector := trading.NewSlippageProtection(nil, log)
|
|
capitalOptimizer := arbitrum.NewCapitalOptimizer(log)
|
|
profitTracker := arbitrum.NewProfitabilityTracker(log)
|
|
|
|
// Create stress test suite
|
|
suite := NewStressTestSuite(
|
|
log,
|
|
protocolRegistry,
|
|
poolCache,
|
|
marketDiscovery,
|
|
strategyEngine,
|
|
profitCalculator,
|
|
mevAnalyzer,
|
|
slippageProtector,
|
|
capitalOptimizer,
|
|
profitTracker,
|
|
)
|
|
|
|
// Generate test data
|
|
testPools := suite.generateTestPools(1000)
|
|
testEvents := suite.generateTestEvents(10000)
|
|
|
|
b.ResetTimer()
|
|
b.Run("ConcurrentPoolScanning", func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
var wg sync.WaitGroup
|
|
errorCount := int64(0)
|
|
|
|
// Process pools concurrently
|
|
for _, pool := range testPools {
|
|
wg.Add(1)
|
|
go func(p *market.CachedData) {
|
|
defer wg.Done()
|
|
|
|
// Simulate pool scanning
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
|
|
// Mock pool scanning operation
|
|
err := suite.mockPoolScan(ctx, p)
|
|
if err != nil {
|
|
atomic.AddInt64(&errorCount, 1)
|
|
}
|
|
}(pool)
|
|
}
|
|
|
|
// Wait for all operations to complete
|
|
wg.Wait()
|
|
|
|
if errorCount > 0 {
|
|
b.Fatalf("Concurrent pool scanning failed with %d errors", errorCount)
|
|
}
|
|
}
|
|
})
|
|
|
|
b.Run("ConcurrentEventProcessing", func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
var wg sync.WaitGroup
|
|
errorCount := int64(0)
|
|
|
|
// Process events concurrently
|
|
for _, event := range testEvents {
|
|
wg.Add(1)
|
|
go func(e events.Event) {
|
|
defer wg.Done()
|
|
|
|
// Simulate event processing
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
|
|
// Mock event processing operation
|
|
err := suite.mockEventProcessing(ctx, e)
|
|
if err != nil {
|
|
atomic.AddInt64(&errorCount, 1)
|
|
}
|
|
}(event)
|
|
}
|
|
|
|
// Wait for all operations to complete
|
|
wg.Wait()
|
|
|
|
if errorCount > 0 {
|
|
b.Fatalf("Concurrent event processing failed with %d errors", errorCount)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
// BenchmarkMemoryAllocation benchmarks memory allocation performance
|
|
func BenchmarkMemoryAllocation(b *testing.B) {
|
|
// Create test logger
|
|
log := logger.New("warn", "text", "") // Use warn level to minimize logging overhead
|
|
|
|
// Create test components
|
|
protocolRegistry := arbitrum.NewArbitrumProtocolRegistry(log)
|
|
poolCache := pools.NewPoolCache(10000, time.Hour)
|
|
marketDiscovery := market.NewMarketDiscovery(nil, log, "")
|
|
strategyEngine := arbitrum.NewMEVStrategyEngine(log, protocolRegistry)
|
|
profitCalculator := profitcalc.NewProfitCalculatorWithClient(log, nil)
|
|
mevAnalyzer := arbitrum.NewMEVAnalyzer(log)
|
|
slippageProtector := trading.NewSlippageProtection(nil, log)
|
|
capitalOptimizer := arbitrum.NewCapitalOptimizer(log)
|
|
profitTracker := arbitrum.NewProfitabilityTracker(log)
|
|
|
|
// Create stress test suite
|
|
suite := NewStressTestSuite(
|
|
log,
|
|
protocolRegistry,
|
|
poolCache,
|
|
marketDiscovery,
|
|
strategyEngine,
|
|
profitCalculator,
|
|
mevAnalyzer,
|
|
slippageProtector,
|
|
capitalOptimizer,
|
|
profitTracker,
|
|
)
|
|
|
|
b.ResetTimer()
|
|
b.Run("LargeDataStructures", func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
// Generate large test data sets
|
|
dataSets := suite.generateLargeTestDataSets(10000)
|
|
|
|
// Process large data sets
|
|
var wg sync.WaitGroup
|
|
errorCount := int64(0)
|
|
|
|
for _, dataSet := range dataSets {
|
|
wg.Add(1)
|
|
go func(ds []*market.CachedData) {
|
|
defer wg.Done()
|
|
|
|
// Simulate memory-intensive processing
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
|
|
// Mock memory-intensive operation
|
|
err := suite.mockMemoryIntensiveProcessing(ctx, ds)
|
|
if err != nil {
|
|
atomic.AddInt64(&errorCount, 1)
|
|
}
|
|
}(dataSet)
|
|
}
|
|
|
|
// Wait for all operations to complete
|
|
wg.Wait()
|
|
|
|
if errorCount > 0 {
|
|
b.Fatalf("Memory-intensive processing failed with %d errors", errorCount)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
// BenchmarkCPUUtilization benchmarks CPU utilization performance
|
|
func BenchmarkCPUUtilization(b *testing.B) {
|
|
// Create test logger
|
|
log := logger.New("warn", "text", "") // Use warn level to minimize logging overhead
|
|
|
|
// Create test components
|
|
protocolRegistry := arbitrum.NewArbitrumProtocolRegistry(log)
|
|
poolCache := pools.NewPoolCache(10000, time.Hour)
|
|
marketDiscovery := market.NewMarketDiscovery(nil, log, "")
|
|
strategyEngine := arbitrum.NewMEVStrategyEngine(log, protocolRegistry)
|
|
profitCalculator := profitcalc.NewProfitCalculatorWithClient(log, nil)
|
|
mevAnalyzer := arbitrum.NewMEVAnalyzer(log)
|
|
slippageProtector := trading.NewSlippageProtection(nil, log)
|
|
capitalOptimizer := arbitrum.NewCapitalOptimizer(log)
|
|
profitTracker := arbitrum.NewProfitabilityTracker(log)
|
|
|
|
// Create stress test suite
|
|
suite := NewStressTestSuite(
|
|
log,
|
|
protocolRegistry,
|
|
poolCache,
|
|
marketDiscovery,
|
|
strategyEngine,
|
|
profitCalculator,
|
|
mevAnalyzer,
|
|
slippageProtector,
|
|
capitalOptimizer,
|
|
profitTracker,
|
|
)
|
|
|
|
b.ResetTimer()
|
|
b.Run("CPUIntensiveCalculations", func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
// Generate test data
|
|
testProfits := suite.generateTestProfits(1000)
|
|
|
|
// Process profits concurrently
|
|
var wg sync.WaitGroup
|
|
errorCount := int64(0)
|
|
|
|
for _, profit := range testProfits {
|
|
wg.Add(1)
|
|
go func(p *arbitrum.ArbitrageOpportunityDetailed) {
|
|
defer wg.Done()
|
|
|
|
// Simulate CPU-intensive calculations
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
|
|
// Mock profit calculation operation
|
|
err := suite.mockProfitCalculation(ctx, p)
|
|
if err != nil {
|
|
atomic.AddInt64(&errorCount, 1)
|
|
}
|
|
}(profit)
|
|
}
|
|
|
|
// Wait for all operations to complete
|
|
wg.Wait()
|
|
|
|
if errorCount > 0 {
|
|
b.Fatalf("CPU-intensive calculations failed with %d errors", errorCount)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
// BenchmarkNetworkLatency benchmarks network latency handling
|
|
func BenchmarkNetworkLatency(b *testing.B) {
|
|
// Create test logger
|
|
log := logger.New("warn", "text", "") // Use warn level to minimize logging overhead
|
|
|
|
// Create test components
|
|
protocolRegistry := arbitrum.NewArbitrumProtocolRegistry(log)
|
|
poolCache := pools.NewPoolCache(10000, time.Hour)
|
|
marketDiscovery := market.NewMarketDiscovery(nil, log, "")
|
|
strategyEngine := arbitrum.NewMEVStrategyEngine(log, protocolRegistry)
|
|
profitCalculator := profitcalc.NewProfitCalculatorWithClient(log, nil)
|
|
mevAnalyzer := arbitrum.NewMEVAnalyzer(log)
|
|
slippageProtector := trading.NewSlippageProtection(nil, log)
|
|
capitalOptimizer := arbitrum.NewCapitalOptimizer(log)
|
|
profitTracker := arbitrum.NewProfitabilityTracker(log)
|
|
|
|
// Create stress test suite
|
|
suite := NewStressTestSuite(
|
|
log,
|
|
protocolRegistry,
|
|
poolCache,
|
|
marketDiscovery,
|
|
strategyEngine,
|
|
profitCalculator,
|
|
mevAnalyzer,
|
|
slippageProtector,
|
|
capitalOptimizer,
|
|
profitTracker,
|
|
)
|
|
|
|
b.ResetTimer()
|
|
b.Run("NetworkRequestHandling", func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
// Generate test data
|
|
testPools := suite.generateTestPools(100)
|
|
|
|
// Process pools with simulated network delays
|
|
var wg sync.WaitGroup
|
|
errorCount := int64(0)
|
|
|
|
for _, pool := range testPools {
|
|
wg.Add(1)
|
|
go func(p *market.CachedData) {
|
|
defer wg.Done()
|
|
|
|
// Simulate network delay
|
|
time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond)
|
|
|
|
// Simulate pool scanning with network request
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
|
|
// Mock pool scanning operation
|
|
err := suite.mockPoolScan(ctx, p)
|
|
if err != nil {
|
|
atomic.AddInt64(&errorCount, 1)
|
|
}
|
|
}(pool)
|
|
}
|
|
|
|
// Wait for all operations to complete
|
|
wg.Wait()
|
|
|
|
if errorCount > 0 {
|
|
b.Fatalf("Network request handling failed with %d errors", errorCount)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
// BenchmarkErrorHandling benchmarks error handling performance
|
|
func BenchmarkErrorHandling(b *testing.B) {
|
|
// Create test logger
|
|
log := logger.New("warn", "text", "") // Use warn level to minimize logging overhead
|
|
|
|
// Create test components
|
|
protocolRegistry := arbitrum.NewArbitrumProtocolRegistry(log)
|
|
poolCache := pools.NewPoolCache(10000, time.Hour)
|
|
marketDiscovery := market.NewMarketDiscovery(nil, log, "")
|
|
strategyEngine := arbitrum.NewMEVStrategyEngine(log, protocolRegistry)
|
|
profitCalculator := profitcalc.NewProfitCalculatorWithClient(log, nil)
|
|
mevAnalyzer := arbitrum.NewMEVAnalyzer(log)
|
|
slippageProtector := trading.NewSlippageProtection(nil, log)
|
|
capitalOptimizer := arbitrum.NewCapitalOptimizer(log)
|
|
profitTracker := arbitrum.NewProfitabilityTracker(log)
|
|
|
|
// Create stress test suite
|
|
suite := NewStressTestSuite(
|
|
log,
|
|
protocolRegistry,
|
|
poolCache,
|
|
marketDiscovery,
|
|
strategyEngine,
|
|
profitCalculator,
|
|
mevAnalyzer,
|
|
slippageProtector,
|
|
capitalOptimizer,
|
|
profitTracker,
|
|
)
|
|
|
|
b.ResetTimer()
|
|
b.Run("ErrorHandlingPerformance", func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
// Generate test data with errors
|
|
testEvents := suite.generateTestEventsWithErrorRate(1000, 0.05) // 5% error rate
|
|
|
|
// Process events with error handling
|
|
var wg sync.WaitGroup
|
|
errorCount := int64(0)
|
|
successCount := int64(0)
|
|
|
|
for _, event := range testEvents {
|
|
wg.Add(1)
|
|
go func(e events.Event) {
|
|
defer wg.Done()
|
|
|
|
// Simulate event processing
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
|
|
// Mock event processing operation
|
|
err := suite.mockEventProcessing(ctx, e)
|
|
if err != nil {
|
|
atomic.AddInt64(&errorCount, 1)
|
|
suite.recordError(fmt.Sprintf("EventProcessingError: %v", err))
|
|
} else {
|
|
atomic.AddInt64(&successCount, 1)
|
|
}
|
|
}(event)
|
|
}
|
|
|
|
// Wait for all operations to complete
|
|
wg.Wait()
|
|
|
|
// Validate error handling worked correctly
|
|
if errorCount+successCount != int64(len(testEvents)) {
|
|
b.Fatalf("Error handling count mismatch: %d errors + %d successes != %d total", errorCount, successCount, len(testEvents))
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
// generateTestEventsWithErrorRate generates test events with a specific error rate
|
|
func (suite *StressTestSuite) generateTestEventsWithErrorRate(count int, errorRate float64) []events.Event {
|
|
events := suite.generateTestEvents(count)
|
|
|
|
// Mark some events to cause errors based on error rate
|
|
errorEvents := int(float64(count) * errorRate)
|
|
for i := 0; i < errorEvents && i < count; i++ {
|
|
// Mark event to cause error
|
|
events[i].Type = events.Unknown // Invalid event type to cause processing errors
|
|
}
|
|
|
|
return events
|
|
}
|
|
|
|
// generateTestPools generates test pools for benchmarking
|
|
func (suite *StressTestSuite) generateTestPools(count int) []*market.CachedData {
|
|
pools := make([]*market.CachedData, count)
|
|
|
|
// Known token addresses for testing
|
|
wethAddr := common.HexToAddress("0x82af49447d8a07e3bd95bd0d56f35241523fbab1")
|
|
usdcAddr := common.HexToAddress("0xaf88d065e77c8cc2239327c5edb3a432268e5831")
|
|
usdtAddr := common.HexToAddress("0xff970a61a04b1ca14834a43f5de4533ebddb5cc8")
|
|
wbtcAddr := common.HexToAddress("0x2f2a2543b76a4166549f7aab2e75bef0aefc5b0f")
|
|
|
|
tokens := []common.Address{wethAddr, usdcAddr, usdtAddr, wbtcAddr}
|
|
|
|
for i := 0; i < count; i++ {
|
|
// Select random tokens for the pool
|
|
token0 := tokens[rand.Intn(len(tokens))]
|
|
token1 := tokens[rand.Intn(len(tokens))]
|
|
for token0 == token1 {
|
|
token1 = tokens[rand.Intn(len(tokens))]
|
|
}
|
|
|
|
// Create deterministic pool address based on index
|
|
poolAddr := common.BigToAddress(big.NewInt(int64(i + 1000000)))
|
|
|
|
// Generate deterministic liquidity and price values
|
|
liquidity := uint256.NewInt(uint64(1000000 + i*1000)) // Increasing liquidity
|
|
sqrtPrice := uint256.NewInt(uint64(1000000000000000000 + i*100000000000000)) // Increasing price
|
|
|
|
pools[i] = &market.CachedData{
|
|
Address: poolAddr,
|
|
Token0: token0,
|
|
Token1: token1,
|
|
Fee: int64(3000 + (i%4)*500), // Varying fees (0.05%, 0.3%, 0.5%, 1%)
|
|
Liquidity: liquidity,
|
|
SqrtPriceX96: sqrtPrice,
|
|
Tick: int(74959 + i), // Varying ticks
|
|
TickSpacing: 60,
|
|
Protocol: fmt.Sprintf("uniswap_v%d", 2+(i%2)), // Alternating V2/V3
|
|
LastUpdated: time.Now(),
|
|
}
|
|
}
|
|
|
|
return pools
|
|
}
|
|
|
|
// generateTestEvents generates test events for benchmarking
|
|
func (suite *StressTestSuite) generateTestEvents(count int) []events.Event {
|
|
events := make([]events.Event, count)
|
|
|
|
// Known token addresses for testing
|
|
wethAddr := common.HexToAddress("0x82af49447d8a07e3bd95bd0d56f35241523fbab1")
|
|
usdcAddr := common.HexToAddress("0xaf88d065e77c8cc2239327c5edb3a432268e5831")
|
|
usdtAddr := common.HexToAddress("0xff970a61a04b1ca14834a43f5de4533ebddb5cc8")
|
|
wbtcAddr := common.HexToAddress("0x2f2a2543b76a4166549f7aab2e75bef0aefc5b0f")
|
|
|
|
tokens := []common.Address{wethAddr, usdcAddr, usdtAddr, wbtcAddr}
|
|
protocols := []string{"uniswap_v2", "uniswap_v3", "sushiswap", "camelot_v2", "camelot_v3", "balancer_v2", "curve", "algebra"}
|
|
|
|
for i := 0; i < count; i++ {
|
|
// Select random tokens for the event
|
|
token0 := tokens[rand.Intn(len(tokens))]
|
|
token1 := tokens[rand.Intn(len(tokens))]
|
|
for token0 == token1 {
|
|
token1 = tokens[rand.Intn(len(tokens))]
|
|
}
|
|
|
|
// Select random protocol
|
|
protocol := protocols[rand.Intn(len(protocols))]
|
|
|
|
// Create deterministic pool address based on index
|
|
poolAddr := common.BigToAddress(big.NewInt(int64(i + 2000000)))
|
|
|
|
// Generate deterministic amounts
|
|
amount0 := big.NewInt(int64(100000000000000000 + int64(i)*10000000000000)) // Varying amounts
|
|
amount1 := big.NewInt(int64(200000000000000000 + int64(i)*20000000000000)) // Varying amounts
|
|
|
|
// Generate deterministic liquidity and price values
|
|
liquidity := uint256.NewInt(uint64(500000 + i*500)) // Increasing liquidity
|
|
sqrtPrice := uint256.NewInt(uint64(500000000000000000 + i*50000000000000)) // Increasing price
|
|
|
|
events[i] = events.Event{
|
|
Timestamp: time.Now(),
|
|
BlockNumber: uint64(10000000 + i),
|
|
TransactionHash: common.BigToHash(big.NewInt(int64(i + 3000000))),
|
|
LogIndex: uint(i % 100),
|
|
Type: events.Swap, // Default to swap events
|
|
Protocol: protocol,
|
|
PoolAddress: poolAddr,
|
|
Token0: token0,
|
|
Token1: token1,
|
|
Amount0: amount0,
|
|
Amount1: amount1,
|
|
Liquidity: liquidity,
|
|
SqrtPriceX96: sqrtPrice,
|
|
Tick: int32(74959 + i%1000), // Varying ticks
|
|
}
|
|
}
|
|
|
|
return events
|
|
}
|
|
|
|
// generateTestProfits generates test profits for benchmarking
|
|
func (suite *StressTestSuite) generateTestProfits(count int) []*arbitrum.ArbitrageOpportunityDetailed {
|
|
profits := make([]*arbitrum.ArbitrageOpportunityDetailed, count)
|
|
|
|
// Known token addresses for testing
|
|
wethAddr := common.HexToAddress("0x82af49447d8a07e3bd95bd0d56f35241523fbab1")
|
|
usdcAddr := common.HexToAddress("0xaf88d065e77c8cc2239327c5edb3a432268e5831")
|
|
usdtAddr := common.HexToAddress("0xff970a61a04b1ca14834a43f5de4533ebddb5cc8")
|
|
wbtcAddr := common.HexToAddress("0x2f2a2543b76a4166549f7aab2e75bef0aefc5b0f")
|
|
|
|
tokens := []common.Address{wethAddr, usdcAddr, usdtAddr, wbtcAddr}
|
|
exchanges := []string{"uniswap_v2", "uniswap_v3", "sushiswap", "camelot_v2", "camelot_v3", "balancer_v2", "curve", "algebra"}
|
|
|
|
for i := 0; i < count; i++ {
|
|
// Select random tokens for the arbitrage
|
|
tokenIn := tokens[rand.Intn(len(tokens))]
|
|
tokenOut := tokens[rand.Intn(len(tokens))]
|
|
for tokenIn == tokenOut {
|
|
tokenOut = tokens[rand.Intn(len(tokens))]
|
|
}
|
|
|
|
// Select random exchanges
|
|
exchangeA := exchanges[rand.Intn(len(exchanges))]
|
|
exchangeB := exchanges[rand.Intn(len(exchanges))]
|
|
for exchangeA == exchangeB {
|
|
exchangeB = exchanges[rand.Intn(len(exchanges))]
|
|
}
|
|
|
|
// Create deterministic pool addresses based on index
|
|
poolA := common.BigToAddress(big.NewInt(int64(i + 4000000)))
|
|
poolB := common.BigToAddress(big.NewInt(int64(i + 5000000)))
|
|
|
|
// Generate deterministic amounts
|
|
amountIn := big.NewInt(int64(100000000000000000 + int64(i)*10000000000000)) // Varying amounts
|
|
expectedAmountOut := big.NewInt(int64(105000000000000000 + int64(i)*10500000000000)) // 5% profit
|
|
actualAmountOut := big.NewInt(int64(104000000000000000 + int64(i)*10400000000000)) // 4% profit
|
|
profit := big.NewInt(int64(4000000000000000 + int64(i)*400000000000)) // Varying profits
|
|
gasCost := big.NewInt(int64(1000000000000000 + int64(i)*100000000000)) // Varying gas costs
|
|
netProfit := new(big.Int).Sub(profit, gasCost)
|
|
|
|
profits[i] = &arbitrum.ArbitrageOpportunityDetailed{
|
|
ID: fmt.Sprintf("arb_%d_%d", time.Now().Unix(), i+1000000),
|
|
Type: "arbitrage",
|
|
TokenIn: tokenIn,
|
|
TokenOut: tokenOut,
|
|
AmountIn: amountIn,
|
|
ExpectedAmountOut: expectedAmountOut,
|
|
ActualAmountOut: actualAmountOut,
|
|
Profit: profit,
|
|
ProfitUSD: 50.0 + float64(i%1000)*0.05, // Varying USD profits
|
|
ProfitMargin: 0.04 + float64(i%100)*0.0001, // Varying margins (4-5%)
|
|
GasCost: gasCost,
|
|
NetProfit: netProfit,
|
|
ExchangeA: exchangeA,
|
|
ExchangeB: exchangeB,
|
|
PoolA: poolA,
|
|
PoolB: poolB,
|
|
PriceImpactA: 0.005 + float64(i%1000)*0.000005, // Varying price impacts
|
|
PriceImpactB: 0.003 + float64(i%1000)*0.000003, // Varying price impacts
|
|
CapitalRequired: 100.0 + float64(i%10000)*0.01, // Varying capital requirements
|
|
GasCostUSD: 5.0 + float64(i%100)*0.05, // Varying gas costs in USD
|
|
Confidence: 0.8 + float64(i%20)*0.01, // Varying confidence (80-100%)
|
|
RiskScore: 0.2 + float64(i%50)*0.01, // Varying risk scores (20-70%)
|
|
ExecutionTime: time.Duration(15+i%10) * time.Second, // Varying execution times
|
|
Timestamp: time.Now(),
|
|
}
|
|
}
|
|
|
|
return profits
|
|
}
|
|
|
|
// generateLargeTestDataSets generates large test data sets for memory benchmarking
|
|
func (suite *StressTestSuite) generateLargeTestDataSets(count int) [][]*market.CachedData {
|
|
// Create batches of test data
|
|
batchSize := 1000
|
|
batchCount := count / batchSize
|
|
if count%batchSize > 0 {
|
|
batchCount++
|
|
}
|
|
|
|
dataSets := make([][]*market.CachedData, batchCount)
|
|
|
|
for i := 0; i < batchCount; i++ {
|
|
dataSets[i] = suite.generateTestPools(batchSize)
|
|
}
|
|
|
|
return dataSets
|
|
}
|
|
|
|
// mockPoolScan simulates pool scanning for benchmarking
|
|
func (suite *StressTestSuite) mockPoolScan(ctx context.Context, pool *market.CachedData) error {
|
|
atomic.AddUint64(&suite.metrics.totalPoolsScanned, 1)
|
|
atomic.AddUint64(&suite.metrics.totalTestsRun, 1)
|
|
|
|
// Simulate work
|
|
select {
|
|
case <-ctx.Done():
|
|
return ctx.Err()
|
|
case <-time.After(time.Duration(rand.Intn(10)) * time.Millisecond):
|
|
// Simulate occasional errors
|
|
if rand.Intn(1000) < 5 { // 0.5% error rate
|
|
atomic.AddUint64(&suite.metrics.testsFailed, 1)
|
|
return fmt.Errorf("simulated pool scan error")
|
|
}
|
|
|
|
atomic.AddUint64(&suite.metrics.testsPassed, 1)
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// mockEventProcessing simulates event processing for benchmarking
|
|
func (suite *StressTestSuite) mockEventProcessing(ctx context.Context, event events.Event) error {
|
|
atomic.AddUint64(&suite.metrics.totalTransactions, 1)
|
|
atomic.AddUint64(&suite.metrics.totalTestsRun, 1)
|
|
|
|
// Simulate work
|
|
select {
|
|
case <-ctx.Done():
|
|
return ctx.Err()
|
|
case <-time.After(time.Duration(rand.Intn(5)) * time.Millisecond):
|
|
// Simulate occasional errors
|
|
if rand.Intn(1000) < 3 { // 0.3% error rate
|
|
atomic.AddUint64(&suite.metrics.testsFailed, 1)
|
|
return fmt.Errorf("simulated event processing error")
|
|
}
|
|
|
|
atomic.AddUint64(&suite.metrics.testsPassed, 1)
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// mockMemoryIntensiveProcessing simulates memory-intensive processing for benchmarking
|
|
func (suite *StressTestSuite) mockMemoryIntensiveProcessing(ctx context.Context, dataSet []*market.CachedData) error {
|
|
atomic.AddUint64(&suite.metrics.totalTestsRun, 1)
|
|
|
|
// Simulate memory-intensive work
|
|
// Create temporary data structures to consume memory
|
|
tempData := make([]*market.CachedData, len(dataSet))
|
|
copy(tempData, dataSet)
|
|
|
|
// Simulate work
|
|
select {
|
|
case <-ctx.Done():
|
|
return ctx.Err()
|
|
case <-time.After(time.Duration(rand.Intn(20)) * time.Millisecond):
|
|
// Clear temporary data
|
|
tempData = nil
|
|
|
|
// Simulate occasional errors
|
|
if rand.Intn(1000) < 8 { // 0.8% error rate
|
|
atomic.AddUint64(&suite.metrics.testsFailed, 1)
|
|
return fmt.Errorf("simulated memory-intensive processing error")
|
|
}
|
|
|
|
atomic.AddUint64(&suite.metrics.testsPassed, 1)
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// mockProfitCalculation simulates profit calculation for benchmarking
|
|
func (suite *StressTestSuite) mockProfitCalculation(ctx context.Context, profit *arbitrum.ArbitrageOpportunityDetailed) error {
|
|
atomic.AddUint64(&suite.metrics.totalArbitrageOps, 1)
|
|
atomic.AddUint64(&suite.metrics.totalTestsRun, 1)
|
|
|
|
// Simulate work
|
|
select {
|
|
case <-ctx.Done():
|
|
return ctx.Err()
|
|
case <-time.After(time.Duration(rand.Intn(15)) * time.Millisecond):
|
|
// Simulate occasional errors
|
|
if rand.Intn(1000) < 6 { // 0.6% error rate
|
|
atomic.AddUint64(&suite.metrics.testsFailed, 1)
|
|
return fmt.Errorf("simulated profit calculation error")
|
|
}
|
|
|
|
atomic.AddUint64(&suite.metrics.testsPassed, 1)
|
|
return nil
|
|
}
|
|
}
|