fix(multicall): resolve critical multicall parsing corruption issues
- 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>
This commit is contained in:
@@ -3,14 +3,19 @@ package transport
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math"
|
||||
"runtime"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/fraktal/mev-beta/internal/logger"
|
||||
"github.com/fraktal/mev-beta/pkg/security"
|
||||
)
|
||||
|
||||
// BenchmarkSuite provides comprehensive performance testing for the transport layer
|
||||
type BenchmarkSuite struct {
|
||||
logger *logger.Logger
|
||||
messageBus *UniversalMessageBus
|
||||
results []BenchmarkResult
|
||||
config BenchmarkConfig
|
||||
@@ -74,8 +79,9 @@ type LatencyTracker struct {
|
||||
}
|
||||
|
||||
// NewBenchmarkSuite creates a new benchmark suite
|
||||
func NewBenchmarkSuite(messageBus *UniversalMessageBus) *BenchmarkSuite {
|
||||
func NewBenchmarkSuite(messageBus *UniversalMessageBus, logger *logger.Logger) *BenchmarkSuite {
|
||||
return &BenchmarkSuite{
|
||||
logger: logger,
|
||||
messageBus: messageBus,
|
||||
results: make([]BenchmarkResult, 0),
|
||||
config: BenchmarkConfig{
|
||||
@@ -268,8 +274,19 @@ func (bs *BenchmarkSuite) runSingleBenchmark(ctx context.Context, transport Tran
|
||||
result.ThroughputByteSec = float64(result.BytesSent) / actualDuration.Seconds()
|
||||
result.ErrorRate = float64(totalErrors) / float64(totalSent) * 100
|
||||
result.CPUUsage = cpuUsage
|
||||
result.MemoryUsage = int64(memUsageAfter.Alloc - memUsageBefore.Alloc)
|
||||
result.GCPauses = int64(memUsageAfter.NumGC - memUsageBefore.NumGC)
|
||||
// Calculate memory usage difference safely
|
||||
memDiff := memUsageAfter.Alloc - memUsageBefore.Alloc
|
||||
memDiffInt64, err := security.SafeUint64ToInt64(memDiff)
|
||||
if err != nil {
|
||||
bs.logger.Warn("Memory usage difference exceeds int64 max", "diff", memDiff, "error", err)
|
||||
result.MemoryUsage = math.MaxInt64
|
||||
} else {
|
||||
result.MemoryUsage = memDiffInt64
|
||||
}
|
||||
|
||||
// Calculate GC pauses difference safely
|
||||
gcDiff := int64(memUsageAfter.NumGC) - int64(memUsageBefore.NumGC)
|
||||
result.GCPauses = gcDiff
|
||||
|
||||
// Calculate latency percentiles
|
||||
if len(latencyTracker.latencies) > 0 {
|
||||
|
||||
@@ -429,30 +429,17 @@ func (pm *ProviderManager) performHealthChecks() {
|
||||
|
||||
// checkProviderHealth performs a health check on a single provider
|
||||
func (pm *ProviderManager) checkProviderHealth(provider *Provider) {
|
||||
if !provider.Config.HealthCheck.Enabled {
|
||||
return
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), provider.Config.HealthCheck.Timeout)
|
||||
defer cancel()
|
||||
|
||||
start := time.Now()
|
||||
var err error
|
||||
|
||||
// Try to get latest block number as health check
|
||||
if provider.HTTPClient != nil {
|
||||
_, err = provider.HTTPClient.BlockNumber(ctx)
|
||||
} else if provider.WSClient != nil {
|
||||
_, err = provider.WSClient.BlockNumber(ctx)
|
||||
}
|
||||
|
||||
provider.mutex.Lock()
|
||||
provider.LastHealthCheck = time.Now()
|
||||
provider.IsHealthy = (err == nil)
|
||||
if err == nil {
|
||||
provider.AvgResponseTime = time.Since(start)
|
||||
}
|
||||
provider.mutex.Unlock()
|
||||
performProviderHealthCheck(provider, func(ctx context.Context, provider *Provider) error {
|
||||
// Try to get latest block number as health check
|
||||
if provider.HTTPClient != nil {
|
||||
_, err := provider.HTTPClient.BlockNumber(ctx)
|
||||
return err
|
||||
} else if provider.WSClient != nil {
|
||||
_, err := provider.WSClient.BlockNumber(ctx)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// collectMetrics collects performance metrics
|
||||
|
||||
@@ -12,6 +12,62 @@ import (
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
)
|
||||
|
||||
// ProviderHealthCheckFunc defines the function signature for performing health checks
|
||||
type ProviderHealthCheckFunc func(ctx context.Context, provider *Provider) error
|
||||
|
||||
// performProviderHealthCheckWithMetrics is a shared utility function that performs health checks on providers
|
||||
// and updates additional metrics for execution pools
|
||||
func performProviderHealthCheckWithMetrics(provider *Provider, checkFunc ProviderHealthCheckFunc) {
|
||||
if !provider.Config.HealthCheck.Enabled {
|
||||
return
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), provider.Config.HealthCheck.Timeout)
|
||||
defer cancel()
|
||||
|
||||
start := time.Now()
|
||||
var err error
|
||||
|
||||
// Execute the provider-specific health check
|
||||
err = checkFunc(ctx, provider)
|
||||
|
||||
provider.mutex.Lock()
|
||||
provider.LastHealthCheck = time.Now()
|
||||
provider.IsHealthy = (err == nil)
|
||||
if err == nil {
|
||||
provider.AvgResponseTime = time.Since(start)
|
||||
}
|
||||
if err != nil {
|
||||
provider.ErrorCount++
|
||||
}
|
||||
provider.RequestCount++
|
||||
provider.mutex.Unlock()
|
||||
}
|
||||
|
||||
// performProviderHealthCheck is a shared utility function that performs health checks on providers
|
||||
func performProviderHealthCheck(provider *Provider, checkFunc ProviderHealthCheckFunc) {
|
||||
if !provider.Config.HealthCheck.Enabled {
|
||||
return
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), provider.Config.HealthCheck.Timeout)
|
||||
defer cancel()
|
||||
|
||||
start := time.Now()
|
||||
var err error
|
||||
|
||||
// Execute the provider-specific health check
|
||||
err = checkFunc(ctx, provider)
|
||||
|
||||
provider.mutex.Lock()
|
||||
provider.LastHealthCheck = time.Now()
|
||||
provider.IsHealthy = (err == nil)
|
||||
if err == nil {
|
||||
provider.AvgResponseTime = time.Since(start)
|
||||
}
|
||||
provider.mutex.Unlock()
|
||||
}
|
||||
|
||||
// ProviderPool interface defines common functionality for all provider pools
|
||||
type ProviderPool interface {
|
||||
GetHTTPClient() (*ethclient.Client, error)
|
||||
@@ -285,30 +341,17 @@ func (p *ReadOnlyProviderPool) performHealthChecks() {
|
||||
|
||||
// checkProviderHealth performs a health check on a single provider
|
||||
func (p *ReadOnlyProviderPool) checkProviderHealth(provider *Provider) {
|
||||
if !provider.Config.HealthCheck.Enabled {
|
||||
return
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), provider.Config.HealthCheck.Timeout)
|
||||
defer cancel()
|
||||
|
||||
start := time.Now()
|
||||
var err error
|
||||
|
||||
// For read-only pool, test both HTTP and WebSocket if available
|
||||
if provider.WSClient != nil {
|
||||
_, err = provider.WSClient.BlockNumber(ctx)
|
||||
} else if provider.HTTPClient != nil {
|
||||
_, err = provider.HTTPClient.BlockNumber(ctx)
|
||||
}
|
||||
|
||||
provider.mutex.Lock()
|
||||
provider.LastHealthCheck = time.Now()
|
||||
provider.IsHealthy = (err == nil)
|
||||
if err == nil {
|
||||
provider.AvgResponseTime = time.Since(start)
|
||||
}
|
||||
provider.mutex.Unlock()
|
||||
performProviderHealthCheck(provider, func(ctx context.Context, provider *Provider) error {
|
||||
// For read-only pool, test both HTTP and WebSocket if available
|
||||
if provider.WSClient != nil {
|
||||
_, err := provider.WSClient.BlockNumber(ctx)
|
||||
return err
|
||||
} else if provider.HTTPClient != nil {
|
||||
_, err := provider.HTTPClient.BlockNumber(ctx)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// GetStats returns current read-only provider pool statistics
|
||||
@@ -659,32 +702,14 @@ func (p *ExecutionProviderPool) performHealthChecks() {
|
||||
|
||||
// checkProviderHealth performs a health check on a single provider
|
||||
func (p *ExecutionProviderPool) checkProviderHealth(provider *Provider) {
|
||||
if !provider.Config.HealthCheck.Enabled {
|
||||
return
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), provider.Config.HealthCheck.Timeout)
|
||||
defer cancel()
|
||||
|
||||
start := time.Now()
|
||||
var err error
|
||||
|
||||
// For execution pool, test HTTP client reliability
|
||||
if provider.HTTPClient != nil {
|
||||
_, err = provider.HTTPClient.BlockNumber(ctx)
|
||||
}
|
||||
|
||||
provider.mutex.Lock()
|
||||
provider.LastHealthCheck = time.Now()
|
||||
provider.IsHealthy = (err == nil)
|
||||
if err == nil {
|
||||
provider.AvgResponseTime = time.Since(start)
|
||||
}
|
||||
if err != nil {
|
||||
provider.ErrorCount++
|
||||
}
|
||||
provider.RequestCount++
|
||||
provider.mutex.Unlock()
|
||||
performProviderHealthCheckWithMetrics(provider, func(ctx context.Context, provider *Provider) error {
|
||||
// For execution pool, test HTTP client reliability
|
||||
if provider.HTTPClient != nil {
|
||||
_, err := provider.HTTPClient.BlockNumber(ctx)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// GetStats returns current execution provider pool statistics
|
||||
@@ -1115,30 +1140,17 @@ func (p *TestingProviderPool) performHealthChecks() {
|
||||
|
||||
// checkProviderHealth performs a health check on a single provider
|
||||
func (p *TestingProviderPool) checkProviderHealth(provider *Provider) {
|
||||
if !provider.Config.HealthCheck.Enabled {
|
||||
return
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), provider.Config.HealthCheck.Timeout)
|
||||
defer cancel()
|
||||
|
||||
start := time.Now()
|
||||
var err error
|
||||
|
||||
// For testing pool, test the most appropriate client
|
||||
if provider.HTTPClient != nil {
|
||||
_, err = provider.HTTPClient.BlockNumber(ctx)
|
||||
} else if provider.WSClient != nil {
|
||||
_, err = provider.WSClient.BlockNumber(ctx)
|
||||
}
|
||||
|
||||
provider.mutex.Lock()
|
||||
provider.LastHealthCheck = time.Now()
|
||||
provider.IsHealthy = (err == nil)
|
||||
if err == nil {
|
||||
provider.AvgResponseTime = time.Since(start)
|
||||
}
|
||||
provider.mutex.Unlock()
|
||||
performProviderHealthCheck(provider, func(ctx context.Context, provider *Provider) error {
|
||||
// For testing pool, test the most appropriate client
|
||||
if provider.HTTPClient != nil {
|
||||
_, err := provider.HTTPClient.BlockNumber(ctx)
|
||||
return err
|
||||
} else if provider.WSClient != nil {
|
||||
_, err := provider.WSClient.BlockNumber(ctx)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// GetStats returns current testing provider pool statistics
|
||||
|
||||
@@ -237,7 +237,7 @@ func (tt *TCPTransport) startServer() error {
|
||||
}
|
||||
|
||||
func (tt *TCPTransport) connectToServer(ctx context.Context) error {
|
||||
addr := fmt.Sprintf("%s:%d", tt.address, tt.port)
|
||||
addr := net.JoinHostPort(tt.address, fmt.Sprintf("%d", tt.port))
|
||||
|
||||
var conn net.Conn
|
||||
var err error
|
||||
@@ -339,7 +339,7 @@ func (tt *TCPTransport) handleConnection(connID string, conn net.Conn) {
|
||||
|
||||
// Process complete messages
|
||||
for {
|
||||
msg, remaining, err := tt.extractMessage(messageBuffer)
|
||||
msg, remaining, err := ExtractMessage(messageBuffer)
|
||||
if err != nil {
|
||||
return // Invalid message format
|
||||
}
|
||||
@@ -404,46 +404,6 @@ func (tt *TCPTransport) removeConnection(connID string) {
|
||||
}
|
||||
}
|
||||
|
||||
func (tt *TCPTransport) extractMessage(buffer []byte) (*Message, []byte, error) {
|
||||
// Look for length prefix (format: "length\nmessage_data")
|
||||
newlineIndex := -1
|
||||
for i, b := range buffer {
|
||||
if b == '\n' {
|
||||
newlineIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if newlineIndex == -1 {
|
||||
return nil, buffer, nil // No complete length prefix yet
|
||||
}
|
||||
|
||||
// Parse length
|
||||
lengthStr := string(buffer[:newlineIndex])
|
||||
var messageLength int
|
||||
if _, err := fmt.Sscanf(lengthStr, "%d", &messageLength); err != nil {
|
||||
return nil, nil, fmt.Errorf("invalid length prefix: %s", lengthStr)
|
||||
}
|
||||
|
||||
// Check if we have the complete message
|
||||
messageStart := newlineIndex + 1
|
||||
messageEnd := messageStart + messageLength
|
||||
if len(buffer) < messageEnd {
|
||||
return nil, buffer, nil // Incomplete message
|
||||
}
|
||||
|
||||
// Extract and parse message
|
||||
messageData := buffer[messageStart:messageEnd]
|
||||
var msg Message
|
||||
if err := json.Unmarshal(messageData, &msg); err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to unmarshal message: %w", err)
|
||||
}
|
||||
|
||||
// Return message and remaining buffer
|
||||
remaining := buffer[messageEnd:]
|
||||
return &msg, remaining, nil
|
||||
}
|
||||
|
||||
func (tt *TCPTransport) updateSendMetrics(msg *Message, latency time.Duration) {
|
||||
tt.mu.Lock()
|
||||
defer tt.mu.Unlock()
|
||||
|
||||
@@ -275,7 +275,7 @@ func (ut *UnixSocketTransport) handleConnection(connID string, conn net.Conn) {
|
||||
|
||||
// Process complete messages
|
||||
for {
|
||||
msg, remaining, err := ut.extractMessage(messageBuffer)
|
||||
msg, remaining, err := ExtractMessage(messageBuffer)
|
||||
if err != nil {
|
||||
return // Invalid message format
|
||||
}
|
||||
@@ -317,46 +317,6 @@ func (ut *UnixSocketTransport) removeConnection(connID string) {
|
||||
}
|
||||
}
|
||||
|
||||
func (ut *UnixSocketTransport) extractMessage(buffer []byte) (*Message, []byte, error) {
|
||||
// Look for length prefix (format: "length\nmessage_data")
|
||||
newlineIndex := -1
|
||||
for i, b := range buffer {
|
||||
if b == '\n' {
|
||||
newlineIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if newlineIndex == -1 {
|
||||
return nil, buffer, nil // No complete length prefix yet
|
||||
}
|
||||
|
||||
// Parse length
|
||||
lengthStr := string(buffer[:newlineIndex])
|
||||
var messageLength int
|
||||
if _, err := fmt.Sscanf(lengthStr, "%d", &messageLength); err != nil {
|
||||
return nil, nil, fmt.Errorf("invalid length prefix: %s", lengthStr)
|
||||
}
|
||||
|
||||
// Check if we have the complete message
|
||||
messageStart := newlineIndex + 1
|
||||
messageEnd := messageStart + messageLength
|
||||
if len(buffer) < messageEnd {
|
||||
return nil, buffer, nil // Incomplete message
|
||||
}
|
||||
|
||||
// Extract and parse message
|
||||
messageData := buffer[messageStart:messageEnd]
|
||||
var msg Message
|
||||
if err := json.Unmarshal(messageData, &msg); err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to unmarshal message: %w", err)
|
||||
}
|
||||
|
||||
// Return message and remaining buffer
|
||||
remaining := buffer[messageEnd:]
|
||||
return &msg, remaining, nil
|
||||
}
|
||||
|
||||
func (ut *UnixSocketTransport) updateSendMetrics(msg *Message, latency time.Duration) {
|
||||
ut.mu.Lock()
|
||||
defer ut.mu.Unlock()
|
||||
|
||||
48
pkg/transport/utils.go
Normal file
48
pkg/transport/utils.go
Normal file
@@ -0,0 +1,48 @@
|
||||
package transport
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// ExtractMessage extracts a message from a byte buffer with length prefix format
|
||||
// Format: "length\nmessage_data"
|
||||
func ExtractMessage(buffer []byte) (*Message, []byte, error) {
|
||||
// Look for length prefix (format: "length\nmessage_data")
|
||||
newlineIndex := -1
|
||||
for i, b := range buffer {
|
||||
if b == '\n' {
|
||||
newlineIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if newlineIndex == -1 {
|
||||
return nil, buffer, nil // No complete length prefix yet
|
||||
}
|
||||
|
||||
// Parse length
|
||||
lengthStr := string(buffer[:newlineIndex])
|
||||
var messageLength int
|
||||
if _, err := fmt.Sscanf(lengthStr, "%d", &messageLength); err != nil {
|
||||
return nil, nil, fmt.Errorf("invalid length prefix: %s", lengthStr)
|
||||
}
|
||||
|
||||
// Check if we have the complete message
|
||||
messageStart := newlineIndex + 1
|
||||
messageEnd := messageStart + messageLength
|
||||
if len(buffer) < messageEnd {
|
||||
return nil, buffer, nil // Incomplete message
|
||||
}
|
||||
|
||||
// Extract and parse message
|
||||
messageData := buffer[messageStart:messageEnd]
|
||||
var msg Message
|
||||
if err := json.Unmarshal(messageData, &msg); err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to unmarshal message: %w", err)
|
||||
}
|
||||
|
||||
// Return message and remaining buffer
|
||||
remaining := buffer[messageEnd:]
|
||||
return &msg, remaining, nil
|
||||
}
|
||||
Reference in New Issue
Block a user