import os, sys, hashlib, json sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from datetime import datetime, date from loguru import logger AUDIT_LOG = os.path.join(os.path.dirname(os.path.dirname( os.path.abspath(__file__))), "logs", "audit.jsonl") def compute_daily_root(log_path: str = AUDIT_LOG) -> str: if not os.path.exists(log_path): return hashlib.sha256(b"empty").hexdigest() hashes = [] with open(log_path, "r", encoding="utf-8") as f: for line in f: line = line.strip() if not line: continue try: entry = json.loads(line) today = date.today().isoformat() if entry.get("ts", "").startswith(today): hashes.append(entry.get("hash", "")) except Exception: continue if not hashes: return hashlib.sha256(b"no_entries_today").hexdigest() combined = "|".join(hashes) root_hash = hashlib.sha256(combined.encode()).hexdigest() logger.info(f"[AuditChain] Daily root hash: {root_hash[:16]}... ({len(hashes)} entries)") return root_hash def store_root_hash(root_hash: str, driver) -> bool: if not driver: return False try: with driver.session() as session: session.run( """ MERGE (a:AuditRoot {date: $date}) SET a.root_hash = $hash, a.computed_at = $ts, a.entry_count = $count """, date=date.today().isoformat(), hash=root_hash, ts=datetime.now().isoformat(), count=0, ) logger.success(f"[AuditChain] Root hash stored in Neo4j for {date.today()}") return True except Exception as e: logger.error(f"[AuditChain] Failed to store root hash: {e}") return False def verify_chain(log_path: str = AUDIT_LOG) -> dict: if not os.path.exists(log_path): return {"valid": True, "entries": 0, "message": "No log file yet"} entries = [] broken_at = None with open(log_path, "r", encoding="utf-8") as f: for i, line in enumerate(f): line = line.strip() if not line: continue try: entry = json.loads(line) stored_hash = entry.pop("hash", "") computed = hashlib.sha256( json.dumps(entry, separators=(",", ":")).encode() ).hexdigest() entry["hash"] = stored_hash if stored_hash != computed and i > 0: broken_at = i break entries.append(entry) except Exception: broken_at = i break valid = broken_at is None return { "valid": valid, "entries": len(entries), "broken_at": broken_at, "message": "Chain intact" if valid else f"Chain broken at entry {broken_at}", } if __name__ == "__main__": print("=" * 55) print("BharatGraph - Audit Chain Test") print("=" * 55) root = compute_daily_root() print(f"\n Daily root hash: {root[:32]}...") chain = verify_chain() print(f" Chain valid: {chain['valid']}") print(f" Entries: {chain['entries']}") print(f" Message: {chain['message']}") print("\nDone!")