package dht import ( "context" "fmt" "strings" "sync" "time" bzzconfig "chorus.services/bzzz/pkg/config" ) // RealDHT implements DHT interface - simplified implementation for Phase 2 // In production, this would use libp2p Kademlia DHT type RealDHT struct { config *bzzconfig.HybridConfig ctx context.Context cancel context.CancelFunc // Simplified storage for Phase 2 storage map[string][]byte providers map[string][]string storageMu sync.RWMutex // Statistics stats *RealDHTStats statsMu sync.RWMutex logger Logger } // RealDHTStats tracks real DHT performance metrics type RealDHTStats struct { ConnectedPeers int `json:"connected_peers"` TotalKeys int `json:"total_keys"` TotalProviders int `json:"total_providers"` BootstrapNodes []string `json:"bootstrap_nodes"` NodeID string `json:"node_id"` Addresses []string `json:"addresses"` Uptime time.Duration `json:"uptime_seconds"` LastBootstrap time.Time `json:"last_bootstrap"` // Operation counters PutOperations uint64 `json:"put_operations"` GetOperations uint64 `json:"get_operations"` ProvideOperations uint64 `json:"provide_operations"` FindProviderOps uint64 `json:"find_provider_operations"` // Performance metrics AvgLatency time.Duration `json:"avg_latency_ms"` ErrorCount uint64 `json:"error_count"` ErrorRate float64 `json:"error_rate"` } // NewRealDHT creates a new simplified real DHT implementation for Phase 2 func NewRealDHT(config *bzzconfig.HybridConfig) (DHT, error) { ctx, cancel := context.WithCancel(context.Background()) realDHT := &RealDHT{ config: config, ctx: ctx, cancel: cancel, storage: make(map[string][]byte), providers: make(map[string][]string), stats: &RealDHTStats{ BootstrapNodes: config.GetDHTBootstrapNodes(), NodeID: "real-dht-node-" + fmt.Sprintf("%d", time.Now().Unix()), Addresses: []string{"127.0.0.1:8080"}, // Simplified for Phase 2 LastBootstrap: time.Now(), }, logger: &defaultLogger{}, } // Simulate bootstrap process if err := realDHT.bootstrap(); err != nil { realDHT.logger.Warn("DHT bootstrap failed", "error", err) // Don't fail completely - DHT can still work without bootstrap } realDHT.logger.Info("Real DHT initialized (Phase 2 simplified)", "node_id", realDHT.stats.NodeID, "bootstrap_nodes", config.GetDHTBootstrapNodes()) return realDHT, nil } // PutValue stores a key-value pair in the DHT func (r *RealDHT) PutValue(ctx context.Context, key string, value []byte) error { start := time.Now() defer func() { r.updateStats("put", time.Since(start), nil) }() // Simulate network latency for real DHT time.Sleep(10 * time.Millisecond) r.storageMu.Lock() r.storage[key] = make([]byte, len(value)) copy(r.storage[key], value) r.storageMu.Unlock() r.logger.Debug("Real DHT PutValue successful", "key", key, "size", len(value)) return nil } // GetValue retrieves a value by key from the DHT func (r *RealDHT) GetValue(ctx context.Context, key string) ([]byte, error) { start := time.Now() // Simulate network latency for real DHT time.Sleep(15 * time.Millisecond) r.storageMu.RLock() value, exists := r.storage[key] r.storageMu.RUnlock() latency := time.Since(start) if !exists { r.updateStats("get", latency, ErrNotFound) return nil, ErrNotFound } // Return a copy to avoid data races result := make([]byte, len(value)) copy(result, value) r.updateStats("get", latency, nil) r.logger.Debug("Real DHT GetValue successful", "key", key, "size", len(result)) return result, nil } // Provide announces that this node provides a value for the given key func (r *RealDHT) Provide(ctx context.Context, key, providerId string) error { start := time.Now() defer func() { r.updateStats("provide", time.Since(start), nil) }() // Simulate network latency for real DHT time.Sleep(5 * time.Millisecond) r.storageMu.Lock() if r.providers[key] == nil { r.providers[key] = make([]string, 0) } // Add provider if not already present found := false for _, p := range r.providers[key] { if p == providerId { found = true break } } if !found { r.providers[key] = append(r.providers[key], providerId) } r.storageMu.Unlock() r.logger.Debug("Real DHT Provide successful", "key", key, "provider_id", providerId) return nil } // FindProviders finds providers for the given key func (r *RealDHT) FindProviders(ctx context.Context, key string) ([]string, error) { start := time.Now() // Simulate network latency for real DHT time.Sleep(20 * time.Millisecond) r.storageMu.RLock() providers, exists := r.providers[key] r.storageMu.RUnlock() var result []string if exists { // Return a copy result = make([]string, len(providers)) copy(result, providers) } else { result = make([]string, 0) } r.updateStats("find_providers", time.Since(start), nil) r.logger.Debug("Real DHT FindProviders successful", "key", key, "provider_count", len(result)) return result, nil } // GetStats returns current DHT statistics func (r *RealDHT) GetStats() DHTStats { r.statsMu.RLock() defer r.statsMu.RUnlock() // Update stats r.storageMu.RLock() keyCount := len(r.storage) providerCount := len(r.providers) r.storageMu.RUnlock() r.stats.TotalKeys = keyCount r.stats.TotalProviders = providerCount r.stats.ConnectedPeers = len(r.config.GetDHTBootstrapNodes()) // Simulate connected peers r.stats.Uptime = time.Since(r.stats.LastBootstrap) // Convert to common DHTStats format return DHTStats{ TotalKeys: r.stats.TotalKeys, TotalPeers: r.stats.ConnectedPeers, Latency: r.stats.AvgLatency, ErrorCount: int(r.stats.ErrorCount), ErrorRate: r.stats.ErrorRate, Uptime: r.stats.Uptime, } } // GetDetailedStats returns real DHT specific statistics func (r *RealDHT) GetDetailedStats() *RealDHTStats { r.statsMu.RLock() defer r.statsMu.RUnlock() // Update dynamic stats r.stats.ConnectedPeers = len(r.host.Network().Peers()) r.stats.Uptime = time.Since(r.stats.LastBootstrap) // Return a copy stats := *r.stats return &stats } // Close shuts down the real DHT func (r *RealDHT) Close() error { r.logger.Info("Shutting down real DHT") r.cancel() // Clean up storage r.storageMu.Lock() r.storage = nil r.providers = nil r.storageMu.Unlock() return nil } // Bootstrap connects to bootstrap nodes and initializes routing table func (r *RealDHT) bootstrap() error { r.logger.Info("Bootstrapping real DHT (Phase 2 simplified)", "bootstrap_nodes", r.config.GetDHTBootstrapNodes()) // Simulate bootstrap process bootstrapNodes := r.config.GetDHTBootstrapNodes() if len(bootstrapNodes) == 0 { r.logger.Warn("No bootstrap nodes configured") } // Simulate connecting to bootstrap nodes time.Sleep(100 * time.Millisecond) // Simulate bootstrap time r.statsMu.Lock() r.stats.LastBootstrap = time.Now() r.stats.ConnectedPeers = len(bootstrapNodes) r.statsMu.Unlock() r.logger.Info("Real DHT bootstrap completed (simulated)", "connected_peers", len(bootstrapNodes)) return nil } // updateStats updates internal performance statistics func (r *RealDHT) updateStats(operation string, latency time.Duration, err error) { r.statsMu.Lock() defer r.statsMu.Unlock() // Update operation counters switch operation { case "put": r.stats.PutOperations++ case "get": r.stats.GetOperations++ case "provide": r.stats.ProvideOperations++ case "find_providers": r.stats.FindProviderOps++ } // Update latency (exponential moving average) totalOps := r.stats.PutOperations + r.stats.GetOperations + r.stats.ProvideOperations + r.stats.FindProviderOps if totalOps > 0 { weight := 1.0 / float64(totalOps) r.stats.AvgLatency = time.Duration(float64(r.stats.AvgLatency)*(1-weight) + float64(latency)*weight) } // Update error statistics if err != nil { r.stats.ErrorCount++ if totalOps > 0 { r.stats.ErrorRate = float64(r.stats.ErrorCount) / float64(totalOps) } } } // defaultLogger provides a basic logger implementation type defaultLogger struct{} func (l *defaultLogger) Info(msg string, fields ...interface{}) { fmt.Printf("[INFO] %s %v\n", msg, fields) } func (l *defaultLogger) Warn(msg string, fields ...interface{}) { fmt.Printf("[WARN] %s %v\n", msg, fields) } func (l *defaultLogger) Error(msg string, fields ...interface{}) { fmt.Printf("[ERROR] %s %v\n", msg, fields) } func (l *defaultLogger) Debug(msg string, fields ...interface{}) { fmt.Printf("[DEBUG] %s %v\n", msg, fields) } // ErrNotFound indicates a key was not found in the DHT var ErrNotFound = fmt.Errorf("key not found")