 95784822ce
			
		
	
	95784822ce
	
	
	
		
			
			@goal: CHORUS-REQ-001 - Fix critical compilation error blocking development
- Remove duplicate type cases for interface{}/any and []interface{}/[]any
- Go 1.18+ treats interface{} and any as identical types
- Standardize on 'any' type for consistency with modern Go practices
- Add proper type conversion for cloneLogMap compatibility
- Include requirement traceability comments
Fixes: CHORUS issue #1
Test: go build ./internal/logging/... passes without errors
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
		
	
		
			
				
	
	
		
			437 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			437 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| 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(),
 | |
| 	}
 | |
| }
 |