package integration import ( "context" "encoding/hex" "fmt" "sync" "testing" "time" "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/assert" "github.com/fraktal/mev-beta/internal/logger" "github.com/fraktal/mev-beta/internal/monitoring" "github.com/fraktal/mev-beta/internal/recovery" "github.com/fraktal/mev-beta/internal/utils" "github.com/fraktal/mev-beta/internal/validation" ) // CorruptionSimulator simulates various corruption scenarios for testing type CorruptionSimulator struct { validator *validation.AddressValidator converter *utils.SafeAddressConverter integrityMonitor *monitoring.IntegrityMonitor errorHandler *recovery.ErrorHandler retryHandler *recovery.RetryHandler logger *logger.Logger } // NewCorruptionSimulator creates a new corruption simulation test environment func NewCorruptionSimulator(t *testing.T) *CorruptionSimulator { log := logger.New("debug", "text", "") validator := validation.NewAddressValidator() converter := utils.NewSafeAddressConverter() integrityMonitor := monitoring.NewIntegrityMonitor(log) errorHandler := recovery.NewErrorHandler(log) retryHandler := recovery.NewRetryHandler(log) return &CorruptionSimulator{ validator: validator, converter: converter, integrityMonitor: integrityMonitor, errorHandler: errorHandler, retryHandler: retryHandler, logger: log, } } // CorruptionScenario represents a specific corruption test case type CorruptionScenario struct { Name string CorruptedAddresses []string ValidAddresses []string ExpectedDetections int ExpectedRecoveries int Severity recovery.ErrorSeverity } func TestCorruption_TOKEN_0x000000_Scenarios(t *testing.T) { simulator := NewCorruptionSimulator(t) scenarios := []CorruptionScenario{ { Name: "Critical TOKEN_0x000000 Pattern", CorruptedAddresses: []string{ "0x0000000300000000000000000000000000000000", // Exact pattern "0x0000000100000000000000000000000000000000", // Similar pattern "0x0000000500000000000000000000000000000000", // Variant }, ValidAddresses: []string{ "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1", // WETH "0xaf88d065e77c8cC2239327C5EDb3A432268e5831", // USDC }, ExpectedDetections: 3, Severity: recovery.SeverityCritical, }, { Name: "Mixed Corruption Patterns", CorruptedAddresses: []string{ "0x1234000000000000000000000000000000000000", // Partial corruption "0x0000000000000000000000000000000000000001", // Almost zero "0xabcd567800000000000000000000000000000000", // Trailing zeros }, ValidAddresses: []string{ "0x912CE59144191C1204E64559FE8253a0e49E6548", // ARB "0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f", // WBTC }, ExpectedDetections: 3, Severity: recovery.SeverityHigh, }, { Name: "Subtle Corruption", CorruptedAddresses: []string{ "0x82aF49447D8a07e3bd95BD0d56f35241523fBaZ1", // Invalid hex char "0x82af49447d8a07e3bd95bd0d56f35241523fbab", // Truncated address }, ValidAddresses: []string{ "0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9", // USDT }, ExpectedDetections: 2, Severity: recovery.SeverityMedium, }, } for _, scenario := range scenarios { t.Run(scenario.Name, func(t *testing.T) { simulator.runCorruptionScenario(t, scenario) }) } } func (cs *CorruptionSimulator) runCorruptionScenario(t *testing.T, scenario CorruptionScenario) { // Reset monitoring stats cs.integrityMonitor = monitoring.NewIntegrityMonitor(cs.logger) detectedCorruptions := 0 successfulRecoveries := 0 // Process corrupted addresses for _, corruptedAddr := range scenario.CorruptedAddresses { cs.logger.Debug("Processing corrupted address", "address", corruptedAddr) // Validate address result := cs.validator.ValidateAddress(corruptedAddr) cs.integrityMonitor.RecordAddressProcessed() if !result.IsValid { detectedCorruptions++ addr := common.HexToAddress(corruptedAddr) cs.integrityMonitor.RecordCorruptionDetected(addr, result.CorruptionScore, "test_scenario") cs.integrityMonitor.RecordValidationResult(false) // Simulate recovery attempt recoveryAction := cs.errorHandler.HandleError( context.Background(), recovery.ErrorTypeAddressCorruption, scenario.Severity, "test_component", addr, "Simulated corruption", map[string]interface{}{"corruption_score": result.CorruptionScore}, ) cs.integrityMonitor.RecordRecoveryAction(recoveryAction) // Count successful recoveries if recoveryAction == recovery.ActionUseFallbackData || recoveryAction == recovery.ActionRetryWithBackoff { successfulRecoveries++ } } else { cs.integrityMonitor.RecordValidationResult(true) } } // Process valid addresses for _, validAddr := range scenario.ValidAddresses { cs.logger.Debug("Processing valid address", "address", validAddr) result := cs.validator.ValidateAddress(validAddr) cs.integrityMonitor.RecordAddressProcessed() if result.IsValid { cs.integrityMonitor.RecordValidationResult(true) } else { t.Errorf("Valid address %s was rejected: %v", validAddr, result.ErrorMessages) } } // Verify results assert.Equal(t, scenario.ExpectedDetections, detectedCorruptions, "Should detect expected number of corruptions") metrics := cs.integrityMonitor.GetMetrics() assert.Equal(t, int64(detectedCorruptions), metrics.CorruptAddressesDetected, "Monitoring should track detected corruptions") totalAddresses := len(scenario.CorruptedAddresses) + len(scenario.ValidAddresses) assert.Equal(t, int64(totalAddresses), metrics.TotalAddressesProcessed, "Should track all processed addresses") // Health score should reflect corruption level expectedHealthScore := 1.0 - (float64(detectedCorruptions) / float64(totalAddresses)) assert.InDelta(t, expectedHealthScore, metrics.HealthScore, 0.2, "Health score should reflect corruption rate") t.Logf("Scenario %s: Detected %d/%d corruptions, Health=%.3f", scenario.Name, detectedCorruptions, scenario.ExpectedDetections, metrics.HighScore) } func TestCorruption_HighVolumeStressTest(t *testing.T) { if testing.Short() { t.Skip("Skipping stress test in short mode") } simulator := NewCorruptionSimulator(t) const ( numWorkers = 20 addressesPerWorker = 1000 corruptionRate = 0.1 // 10% corruption rate ) var wg sync.WaitGroup startTime := time.Now() // Launch worker goroutines for workerID := 0; workerID < numWorkers; workerID++ { wg.Add(1) go func(id int) { defer wg.Done() for i := 0; i < addressesPerWorker; i++ { var address string // Generate corrupted or valid address based on corruption rate if float64(i%100) < corruptionRate*100 { // Generate corrupted address address = generateCorruptedAddress(id, i) } else { // Generate valid-looking address address = generateValidAddress(id, i) } // Process address through validation pipeline result := simulator.validator.ValidateAddress(address) simulator.integrityMonitor.RecordAddressProcessed() if result.IsValid { simulator.integrityMonitor.RecordValidationResult(true) } else { addr := common.HexToAddress(address) simulator.integrityMonitor.RecordCorruptionDetected(addr, result.CorruptionScore, "stress_test") simulator.integrityMonitor.RecordValidationResult(false) // Simulate recovery recoveryAction := simulator.errorHandler.HandleError( context.Background(), recovery.ErrorTypeAddressCorruption, recovery.SeverityMedium, "stress_test", addr, "Stress test corruption", nil, ) simulator.integrityMonitor.RecordRecoveryAction(recoveryAction) } } }(workerID) } // Wait for completion wg.Wait() duration := time.Since(startTime) // Analyze results metrics := simulator.integrityMonitor.GetMetrics() totalAddresses := int64(numWorkers * addressesPerWorker) actualCorruptionRate := float64(metrics.CorruptAddressesDetected) / float64(totalAddresses) t.Logf("Stress Test Results:") t.Logf(" Duration: %v", duration) t.Logf(" Total Addresses: %d", totalAddresses) t.Logf(" Corruptions Detected: %d", metrics.CorruptAddressesDetected) t.Logf(" Actual Corruption Rate: %.2f%%", actualCorruptionRate*100) t.Logf(" Health Score: %.3f", metrics.HealthScore) t.Logf(" Throughput: %.0f addresses/sec", float64(totalAddresses)/duration.Seconds()) // Verify performance and accuracy assert.Equal(t, totalAddresses, metrics.TotalAddressesProcessed) assert.InDelta(t, corruptionRate, actualCorruptionRate, 0.05) // Within 5% of expected assert.Greater(t, metrics.RetryOperationsTriggered, int64(0)) // Should have triggered recoveries // Should process at least 1000 addresses per second throughput := float64(totalAddresses) / duration.Seconds() assert.Greater(t, throughput, 1000.0, "Should maintain high throughput under stress") } func TestCorruption_RecoveryMechanisms(t *testing.T) { simulator := NewCorruptionSimulator(t) testCases := []struct { name string corruptedAddr string errorType recovery.ErrorType severity recovery.ErrorSeverity expectedAction recovery.RecoveryAction }{ { name: "Critical corruption requires fallback", corruptedAddr: "0x0000000300000000000000000000000000000000", errorType: recovery.ErrorTypeAddressCorruption, severity: recovery.SeverityCritical, expectedAction: recovery.ActionUseFallbackData, }, { name: "Medium corruption allows retry", corruptedAddr: "0x123400000000000000000000000000000000000", errorType: recovery.ErrorTypeValidationFailed, severity: recovery.SeverityMedium, expectedAction: recovery.ActionRetryWithBackoff, }, { name: "Low corruption can be skipped", corruptedAddr: "0x82af49447d8a07e3bd95bd0d56f35241523fbab1", // Wrong checksum errorType: recovery.ErrorTypeValidationFailed, severity: recovery.SeverityLow, expectedAction: recovery.ActionSkipAndContinue, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { addr := common.HexToAddress(tc.corruptedAddr) action := simulator.errorHandler.HandleError( context.Background(), tc.errorType, tc.severity, "test_recovery", addr, "Recovery test", nil, ) assert.Equal(t, tc.expectedAction, action, "Should select appropriate recovery action for %s", tc.name) // Record action and verify it's tracked simulator.integrityMonitor.RecordRecoveryAction(action) metrics := simulator.integrityMonitor.GetMetrics() assert.Greater(t, metrics.RecoveryActions[action], int64(0), "Should track recovery action usage") }) } } func TestCorruption_RetryMechanisms(t *testing.T) { simulator := NewCorruptionSimulator(t) // Configure retry for testing retryConfig := recovery.RetryConfig{ MaxAttempts: 3, InitialDelay: 1 * time.Millisecond, MaxDelay: 10 * time.Millisecond, BackoffFactor: 2.0, JitterEnabled: false, TimeoutPerAttempt: 100 * time.Millisecond, } simulator.retryHandler.SetConfig("test_retry", retryConfig) t.Run("Successful retry after corruption", func(t *testing.T) { attempts := 0 operation := func(ctx context.Context, attempt int) error { attempts++ if attempt == 1 { // Simulate corruption on first attempt return fmt.Errorf("address corruption detected") } // Success on retry return nil } result := simulator.retryHandler.ExecuteWithRetry(context.Background(), "test_retry", operation) assert.True(t, result.Success) assert.Equal(t, 2, result.Attempts) assert.Equal(t, 2, attempts) }) t.Run("Persistent corruption fails after max attempts", func(t *testing.T) { attempts := 0 operation := func(ctx context.Context, attempt int) error { attempts++ return fmt.Errorf("persistent corruption") } result := simulator.retryHandler.ExecuteWithRetry(context.Background(), "test_retry", operation) assert.False(t, result.Success) assert.Equal(t, 3, result.Attempts) // Max attempts assert.Equal(t, 3, attempts) }) } func TestCorruption_EndToEndPipeline(t *testing.T) { simulator := NewCorruptionSimulator(t) // Simulate complete transaction processing pipeline testTransactions := []struct { txHash string inputData string addresses []string shouldFail bool }{ { txHash: "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", inputData: "0x022c0d9f000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000", addresses: []string{ "0x0000000300000000000000000000000000000000", // Corrupted "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1", // Valid WETH }, shouldFail: true, // Should fail due to corruption }, { txHash: "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890", inputData: "0x128acb08000000000000000000000000af88d065e77c8cc2239327c5edb3a432268e583100000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1", addresses: []string{ "0xaf88d065e77c8cC2239327C5EDb3A432268e5831", // Valid USDC "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1", // Valid WETH }, shouldFail: false, // Should succeed }, } for _, tx := range testTransactions { t.Run(fmt.Sprintf("Transaction_%s", tx.txHash[:10]), func(t *testing.T) { success := simulator.processTransaction(tx.txHash, tx.inputData, tx.addresses) if tx.shouldFail { assert.False(t, success, "Transaction with corruption should fail") } else { assert.True(t, success, "Transaction with valid addresses should succeed") } }) } // Verify overall system health metrics := simulator.integrityMonitor.GetMetrics() assert.Greater(t, metrics.TotalAddressesProcessed, int64(0)) if metrics.CorruptAddressesDetected > 0 { assert.Greater(t, metrics.RetryOperationsTriggered+metrics.FallbackOperationsUsed, int64(0), "Should have triggered recovery mechanisms for corruption") } } // processTransaction simulates processing a transaction through the full pipeline func (cs *CorruptionSimulator) processTransaction(txHash, inputData string, addresses []string) bool { allValid := true for _, addr := range addresses { cs.integrityMonitor.RecordAddressProcessed() // Validate address result := cs.validator.ValidateAddress(addr) if !result.IsValid { allValid = false // Record corruption address := common.HexToAddress(addr) cs.integrityMonitor.RecordCorruptionDetected(address, result.CorruptionScore, "pipeline_test") cs.integrityMonitor.RecordValidationResult(false) // Attempt recovery recoveryAction := cs.errorHandler.HandleError( context.Background(), recovery.ErrorTypeAddressCorruption, recovery.SeverityCritical, "transaction_processor", address, "Address corruption in transaction", map[string]interface{}{ "tx_hash": txHash, "input_data": inputData, }, ) cs.integrityMonitor.RecordRecoveryAction(recoveryAction) // Only continue if recovery suggests it's safe if recoveryAction == recovery.ActionEmergencyStop || recoveryAction == recovery.ActionCircuitBreaker { return false } } else { cs.integrityMonitor.RecordValidationResult(true) } // Simulate contract call if result.IsValid || result.CorruptionScore < 50 { // Allow low corruption with retry cs.integrityMonitor.RecordContractCallResult(true) } else { cs.integrityMonitor.RecordContractCallResult(false) allValid = false } } return allValid } // Helper functions for generating test addresses func generateCorruptedAddress(workerID, index int) string { patterns := []string{ "0x000000%02d00000000000000000000000000000000", // TOKEN_0x000000 style "0x%04x000000000000000000000000000000000000", // Partial corruption "0x000000000000000000000000000000000000%04x", // Trailing corruption } pattern := patterns[index%len(patterns)] return fmt.Sprintf(pattern, workerID*1000+index) } func generateValidAddress(workerID, index int) string { bytes := make([]byte, common.AddressLength) seed := workerID*97 + index*31 for i := 0; i < len(bytes); i++ { seed = (seed*131 + i*17) & 0xff bytes[i] = byte(seed) } return "0x" + hex.EncodeToString(bytes) }