package logging import ( "context" "crypto/sha256" "encoding/hex" "encoding/json" "fmt" "sync" "time" "chorus/pkg/shhh" "github.com/libp2p/go-libp2p/core/peer" ) // Logger interface for basic logging operations type Logger interface { Info(msg string, args ...interface{}) Warn(msg string, args ...interface{}) Error(msg string, args ...interface{}) } // HypercoreLog represents a simplified Hypercore-inspired distributed log type HypercoreLog struct { entries []LogEntry mutex sync.RWMutex peerID peer.ID // Verification chain headHash string // Replication replicators map[peer.ID]*Replicator redactor *shhh.Sentinel } // LogEntry represents a single entry in the distributed log type LogEntry struct { Index uint64 `json:"index"` Timestamp time.Time `json:"timestamp"` Author string `json:"author"` // Peer ID of the author Type LogType `json:"type"` // Type of log entry Data map[string]interface{} `json:"data"` // Log data Hash string `json:"hash"` // Hash of this entry PrevHash string `json:"prev_hash"` // Hash of previous entry Signature string `json:"signature"` // Digital signature (simplified) } // LogType represents different types of log entries type LogType string const ( // Bzzz coordination logs TaskAnnounced LogType = "task_announced" TaskClaimed LogType = "task_claimed" TaskProgress LogType = "task_progress" TaskCompleted LogType = "task_completed" TaskFailed LogType = "task_failed" // HMMM meta-discussion logs PlanProposed LogType = "plan_proposed" ObjectionRaised LogType = "objection_raised" Collaboration LogType = "collaboration" ConsensusReached LogType = "consensus_reached" Escalation LogType = "escalation" TaskHelpRequested LogType = "task_help_requested" TaskHelpOffered LogType = "task_help_offered" TaskHelpReceived LogType = "task_help_received" // System logs PeerJoined LogType = "peer_joined" PeerLeft LogType = "peer_left" CapabilityBcast LogType = "capability_broadcast" NetworkEvent LogType = "network_event" ) // Replicator handles log replication with other peers type Replicator struct { peerID peer.ID lastSyncIndex uint64 connected bool } // NewHypercoreLog creates a new distributed log for a peer func NewHypercoreLog(peerID peer.ID) *HypercoreLog { return &HypercoreLog{ entries: make([]LogEntry, 0), peerID: peerID, headHash: "", replicators: make(map[peer.ID]*Replicator), } } // SetRedactor wires the SHHH sentinel so log payloads are sanitized before persistence. func (h *HypercoreLog) SetRedactor(redactor *shhh.Sentinel) { h.mutex.Lock() defer h.mutex.Unlock() h.redactor = redactor } // AppendString is a convenience method for string log types (to match interface) func (h *HypercoreLog) AppendString(logType string, data map[string]interface{}) error { _, err := h.Append(LogType(logType), data) return err } // Append adds a new entry to the log func (h *HypercoreLog) Append(logType LogType, data map[string]interface{}) (*LogEntry, error) { h.mutex.Lock() defer h.mutex.Unlock() index := uint64(len(h.entries)) sanitized := h.redactData(logType, data) entry := LogEntry{ Index: index, Timestamp: time.Now(), Author: h.peerID.String(), Type: logType, Data: sanitized, PrevHash: h.headHash, } // Calculate hash entryHash, err := h.calculateEntryHash(entry) if err != nil { return nil, fmt.Errorf("failed to calculate entry hash: %w", err) } entry.Hash = entryHash // Add simple signature (in production, use proper cryptographic signatures) entry.Signature = h.createSignature(entry) // Append to log h.entries = append(h.entries, entry) h.headHash = entryHash fmt.Printf("📝 Log entry appended: %s [%d] by %s\n", logType, index, h.peerID.ShortString()) // Trigger replication to connected peers go h.replicateEntry(entry) return &entry, nil } // Get retrieves a log entry by index func (h *HypercoreLog) Get(index uint64) (*LogEntry, error) { h.mutex.RLock() defer h.mutex.RUnlock() if index >= uint64(len(h.entries)) { return nil, fmt.Errorf("entry %d not found", index) } return &h.entries[index], nil } // Length returns the number of entries in the log func (h *HypercoreLog) Length() uint64 { h.mutex.RLock() defer h.mutex.RUnlock() return uint64(len(h.entries)) } // GetRange retrieves a range of log entries func (h *HypercoreLog) GetRange(start, end uint64) ([]LogEntry, error) { h.mutex.RLock() defer h.mutex.RUnlock() if start >= uint64(len(h.entries)) { return nil, fmt.Errorf("start index %d out of range", start) } if end > uint64(len(h.entries)) { end = uint64(len(h.entries)) } if start > end { return nil, fmt.Errorf("invalid range: start %d > end %d", start, end) } result := make([]LogEntry, end-start) copy(result, h.entries[start:end]) return result, nil } // GetEntriesByType retrieves all entries of a specific type func (h *HypercoreLog) GetEntriesByType(logType LogType) ([]LogEntry, error) { h.mutex.RLock() defer h.mutex.RUnlock() var result []LogEntry for _, entry := range h.entries { if entry.Type == logType { result = append(result, entry) } } return result, nil } // GetEntriesByAuthor retrieves all entries by a specific author func (h *HypercoreLog) GetEntriesByAuthor(author string) ([]LogEntry, error) { h.mutex.RLock() defer h.mutex.RUnlock() var result []LogEntry for _, entry := range h.entries { if entry.Author == author { result = append(result, entry) } } return result, nil } // GetRecentEntries retrieves the most recent N entries from the log func (h *HypercoreLog) GetRecentEntries(count int) ([]LogEntry, error) { h.mutex.RLock() defer h.mutex.RUnlock() totalEntries := len(h.entries) if count <= 0 || totalEntries == 0 { return []LogEntry{}, nil } start := 0 if totalEntries > count { start = totalEntries - count } result := make([]LogEntry, totalEntries-start) copy(result, h.entries[start:]) return result, nil } // GetEntriesSince retrieves all entries since a given index func (h *HypercoreLog) GetEntriesSince(sinceIndex uint64) ([]LogEntry, error) { h.mutex.RLock() defer h.mutex.RUnlock() if sinceIndex >= uint64(len(h.entries)) { return []LogEntry{}, nil } result := make([]LogEntry, len(h.entries)-int(sinceIndex)) copy(result, h.entries[sinceIndex:]) return result, nil } // VerifyIntegrity verifies the integrity of the log chain func (h *HypercoreLog) VerifyIntegrity() error { h.mutex.RLock() defer h.mutex.RUnlock() var prevHash string for i, entry := range h.entries { // Verify previous hash link if entry.PrevHash != prevHash { return fmt.Errorf("integrity error at entry %d: prev_hash mismatch", i) } // Verify entry hash calculatedHash, err := h.calculateEntryHash(entry) if err != nil { return fmt.Errorf("failed to calculate hash for entry %d: %w", i, err) } if entry.Hash != calculatedHash { return fmt.Errorf("integrity error at entry %d: hash mismatch", i) } prevHash = entry.Hash } return nil } // AddReplicator adds a peer for log replication func (h *HypercoreLog) AddReplicator(peerID peer.ID) { h.mutex.Lock() defer h.mutex.Unlock() h.replicators[peerID] = &Replicator{ peerID: peerID, lastSyncIndex: 0, connected: true, } fmt.Printf("🔄 Added replicator: %s\n", peerID.ShortString()) } // RemoveReplicator removes a peer from replication func (h *HypercoreLog) RemoveReplicator(peerID peer.ID) { h.mutex.Lock() defer h.mutex.Unlock() delete(h.replicators, peerID) fmt.Printf("🔄 Removed replicator: %s\n", peerID.ShortString()) } // replicateEntry sends a new entry to all connected replicators func (h *HypercoreLog) replicateEntry(entry LogEntry) { h.mutex.RLock() replicators := make([]*Replicator, 0, len(h.replicators)) for _, replicator := range h.replicators { if replicator.connected { replicators = append(replicators, replicator) } } h.mutex.RUnlock() for _, replicator := range replicators { // In a real implementation, this would send the entry over the network fmt.Printf("🔄 Replicating entry %d to %s\n", entry.Index, replicator.peerID.ShortString()) } } // calculateEntryHash calculates the hash of a log entry func (h *HypercoreLog) calculateEntryHash(entry LogEntry) (string, error) { // Create a copy without the hash and signature for calculation entryForHash := LogEntry{ Index: entry.Index, Timestamp: entry.Timestamp, Author: entry.Author, Type: entry.Type, Data: entry.Data, PrevHash: entry.PrevHash, } entryBytes, err := json.Marshal(entryForHash) if err != nil { return "", err } hash := sha256.Sum256(entryBytes) return hex.EncodeToString(hash[:]), nil } func (h *HypercoreLog) redactData(logType LogType, data map[string]interface{}) map[string]interface{} { cloned := cloneLogMap(data) if cloned == nil { return nil } if h.redactor != nil { labels := map[string]string{ "source": "hypercore", "log_type": string(logType), } h.redactor.RedactMapWithLabels(context.Background(), cloned, labels) } return cloned } func cloneLogMap(in map[string]interface{}) map[string]interface{} { if in == nil { return nil } out := make(map[string]interface{}, len(in)) for k, v := range in { out[k] = cloneLogValue(v) } return out } // @goal: CHORUS-REQ-001 - Fix duplicate type case compilation error // WHY: Go 1.18+ treats interface{} and any as identical types, causing duplicate case errors func cloneLogValue(v interface{}) interface{} { switch tv := v.(type) { case map[string]any: // @goal: CHORUS-REQ-001 - Convert any to interface{} for cloneLogMap compatibility converted := make(map[string]interface{}, len(tv)) for k, val := range tv { converted[k] = val } return cloneLogMap(converted) case []any: converted := make([]interface{}, len(tv)) for i, val := range tv { converted[i] = cloneLogValue(val) } return converted case []string: return append([]string(nil), tv...) default: return tv } } func cloneLogSlice(in []interface{}) []interface{} { out := make([]interface{}, len(in)) for i, val := range in { out[i] = cloneLogValue(val) } return out } // createSignature creates a simplified signature for the entry func (h *HypercoreLog) createSignature(entry LogEntry) string { // In production, this would use proper cryptographic signatures // For now, we use a simple hash-based signature signatureData := fmt.Sprintf("%s:%s:%d", h.peerID.String(), entry.Hash, entry.Index) hash := sha256.Sum256([]byte(signatureData)) return hex.EncodeToString(hash[:])[:16] // Shortened for display } // GetStats returns statistics about the log func (h *HypercoreLog) GetStats() map[string]interface{} { h.mutex.RLock() defer h.mutex.RUnlock() typeCount := make(map[LogType]int) authorCount := make(map[string]int) for _, entry := range h.entries { typeCount[entry.Type]++ authorCount[entry.Author]++ } return map[string]interface{}{ "total_entries": len(h.entries), "head_hash": h.headHash, "replicators": len(h.replicators), "entries_by_type": typeCount, "entries_by_author": authorCount, "peer_id": h.peerID.String(), } }