From 9b6b686a4e9ba97988c1a1adb97528fdfe5c4b24 Mon Sep 17 00:00:00 2001 From: RohanExploit <178623867+RohanExploit@users.noreply.github.com> Date: Tue, 17 Feb 2026 13:59:59 +0000 Subject: [PATCH 1/4] =?UTF-8?q?=E2=9A=A1=20Bolt:=20Optimized=20Blockchain?= =?UTF-8?q?=20Integrity=20Seal=20&=20Issue=20Retrieval?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implemented a robust blockchain-style integrity seal for reports with O(1) verification performance. Key improvements: - Added `previous_integrity_hash` to `Issue` model for direct chain linkage. - Optimized `verify_blockchain_integrity` to use the stored link, avoiding expensive subqueries. - Added an optimized `GET /api/issues/{issue_id}` endpoint with column projection to minimize DB load. - Refactored frontend `VerifyView.jsx` to fetch issue data directly by ID (O(1)) instead of searching through the recent list (O(N)). - Integrated a new "Blockchain Integrity Seal" section in the UI for transparent cryptographic verification. - Updated database migrations to safely add required columns and indexes. Performance Impact: - Verification speed: Reduced from O(Subquery) to O(1). - Frontend issue loading: Improved from O(N) to O(1). - Database efficiency: Reduced bandwidth by using column projection for detailed views. --- backend/init_db.py | 23 +++++--- backend/models.py | 3 +- backend/routers/issues.py | 97 ++++++++++++++++++++++++------- frontend/src/api/issues.js | 8 +++ frontend/src/views/VerifyView.jsx | 73 +++++++++++++++++++++-- 5 files changed, 169 insertions(+), 35 deletions(-) diff --git a/backend/init_db.py b/backend/init_db.py index a2b0f594..faf77dac 100644 --- a/backend/init_db.py +++ b/backend/init_db.py @@ -1,6 +1,8 @@ import sys import os from pathlib import Path +from sqlalchemy import text +import logging # Add project root to path current_file = Path(__file__).resolve() @@ -11,19 +13,13 @@ from backend.database import engine, Base from backend.models import * +logger = logging.getLogger(__name__) + def init_db(): print("Creating tables...") Base.metadata.create_all(bind=engine) print("Tables created.") -if __name__ == "__main__": - init_db() -from sqlalchemy import text -from backend.database import engine -import logging - -logger = logging.getLogger(__name__) - def migrate_db(): """ Perform database migrations. @@ -124,6 +120,13 @@ def migrate_db(): except Exception: pass + # Add previous_integrity_hash column for O(1) blockchain verification + try: + conn.execute(text("ALTER TABLE issues ADD COLUMN previous_integrity_hash VARCHAR")) + print("Migrated database: Added previous_integrity_hash column.") + except Exception: + pass + # Add index on user_email try: conn.execute(text("CREATE INDEX ix_issues_user_email ON issues (user_email)")) @@ -212,3 +215,7 @@ def migrate_db(): logger.info("Database migration check completed.") except Exception as e: logger.error(f"Database migration error: {e}") + +if __name__ == "__main__": + init_db() + migrate_db() diff --git a/backend/models.py b/backend/models.py index 563c1e23..6a82ec53 100644 --- a/backend/models.py +++ b/backend/models.py @@ -161,7 +161,8 @@ class Issue(Base): longitude = Column(Float, nullable=True, index=True) location = Column(String, nullable=True) action_plan = Column(JSONEncodedDict, nullable=True) - integrity_hash = Column(String, nullable=True) # Blockchain integrity seal + integrity_hash = Column(String, nullable=True) # Current report hash + previous_integrity_hash = Column(String, nullable=True) # Link to predecessor for O(1) verification class PushSubscription(Base): __tablename__ = "push_subscriptions" diff --git a/backend/routers/issues.py b/backend/routers/issues.py index 2ad27ca3..b13e4bbd 100644 --- a/backend/routers/issues.py +++ b/backend/routers/issues.py @@ -15,7 +15,7 @@ from backend.models import Issue, PushSubscription from backend.schemas import ( IssueCreateWithDeduplicationResponse, IssueCategory, NearbyIssueResponse, - DeduplicationCheckResponse, IssueSummaryResponse, VoteResponse, + DeduplicationCheckResponse, IssueSummaryResponse, IssueResponse, VoteResponse, IssueStatusUpdateRequest, IssueStatusUpdateResponse, PushSubscriptionRequest, PushSubscriptionResponse, BlockchainVerificationResponse ) @@ -168,15 +168,19 @@ async def create_issue( try: # Save to DB only if no nearby issues found or deduplication failed if deduplication_info is None or not deduplication_info.has_nearby_issues: - # Blockchain feature: calculate integrity hash for the report - # Optimization: Fetch only the last hash to maintain the chain with minimal overhead + # Robust Blockchain Implementation + # 1. Fetch only the last hash to maintain the chain with minimal overhead prev_issue = await run_in_threadpool( lambda: db.query(Issue.integrity_hash).order_by(Issue.id.desc()).first() ) prev_hash = prev_issue[0] if prev_issue and prev_issue[0] else "" -# Simple but effective SHA-256 chaining - hash_content = f"{description}|{category}|{prev_hash}" + # 2. Generate secure reference ID + reference_id = str(uuid.uuid4()) + + # 3. Calculate robust integrity hash incorporating multiple fields + # Chaining logic: hash(ref_id|desc|cat|lat|lon|email|prev_hash) + hash_content = f"{reference_id}|{description}|{category}|{latitude}|{longitude}|{user_email}|{prev_hash}" integrity_hash = hashlib.sha256(hash_content.encode()).hexdigest() # RAG Retrieval (New) @@ -186,7 +190,7 @@ async def create_issue( initial_action_plan = {"relevant_government_rule": relevant_rule} new_issue = Issue( - reference_id=str(uuid.uuid4()), + reference_id=reference_id, description=description, category=category, image_path=image_path, @@ -196,7 +200,8 @@ async def create_issue( longitude=longitude, location=location, action_plan=initial_action_plan, - integrity_hash=integrity_hash + integrity_hash=integrity_hash, + previous_integrity_hash=prev_hash ) # Offload blocking DB operations to threadpool @@ -615,30 +620,42 @@ def get_user_issues( async def verify_blockchain_integrity(issue_id: int, db: Session = Depends(get_db)): """ Verify the cryptographic integrity of a report using the blockchain-style chaining. - Optimized: Uses column projection to fetch only needed data. + Optimized: Uses stored previous_integrity_hash for O(1) chain verification. """ - # Fetch current issue data + # Fetch current issue data (Performance Boost: Fetch only needed columns) current_issue = await run_in_threadpool( lambda: db.query( - Issue.id, Issue.description, Issue.category, Issue.integrity_hash + Issue.id, + Issue.reference_id, + Issue.description, + Issue.category, + Issue.latitude, + Issue.longitude, + Issue.user_email, + Issue.integrity_hash, + Issue.previous_integrity_hash ).filter(Issue.id == issue_id).first() ) if not current_issue: raise HTTPException(status_code=404, detail="Issue not found") - # Fetch previous issue's integrity hash to verify the chain - prev_issue_hash = await run_in_threadpool( - lambda: db.query(Issue.integrity_hash).filter(Issue.id < issue_id).order_by(Issue.id.desc()).first() - ) - - prev_hash = prev_issue_hash[0] if prev_issue_hash and prev_issue_hash[0] else "" + # Chaining logic depends on when the issue was created (legacy fallback) + # Optimized: Use stored previous hash if available, otherwise fallback to subquery (O(N) search) + if current_issue.previous_integrity_hash is not None: + prev_hash = current_issue.previous_integrity_hash + # New robust hash formula + hash_content = f"{current_issue.reference_id}|{current_issue.description}|{current_issue.category}|{current_issue.latitude}|{current_issue.longitude}|{current_issue.user_email}|{prev_hash}" + else: + # Legacy fallback: Fetch previous issue's hash via subquery + prev_issue_hash = await run_in_threadpool( + lambda: db.query(Issue.integrity_hash).filter(Issue.id < issue_id).order_by(Issue.id.desc()).first() + ) + prev_hash = prev_issue_hash[0] if prev_issue_hash and prev_issue_hash[0] else "" + # Legacy hash formula + hash_content = f"{current_issue.description}|{current_issue.category}|{prev_hash}" - # Recompute hash based on current data and previous hash - # Chaining logic: hash(description|category|prev_hash) - hash_content = f"{current_issue.description}|{current_issue.category}|{prev_hash}" computed_hash = hashlib.sha256(hash_content.encode()).hexdigest() - is_valid = (computed_hash == current_issue.integrity_hash) if is_valid: @@ -702,3 +719,43 @@ def get_recent_issues( # Thread-safe cache update recent_issues_cache.set(data, cache_key) return data + +@router.get("/api/issues/{issue_id}", response_model=IssueResponse) +async def get_issue_by_id(issue_id: int, db: Session = Depends(get_db)): + """ + Get a single issue by its ID. + Optimized: Uses column projection for efficient retrieval. + """ + # Performance Boost: Use column projection instead of loading full model + issue = await run_in_threadpool( + lambda: db.query( + Issue.id, + Issue.category, + Issue.description, + Issue.created_at, + Issue.image_path, + Issue.status, + Issue.upvotes, + Issue.location, + Issue.latitude, + Issue.longitude, + Issue.action_plan + ).filter(Issue.id == issue_id).first() + ) + + if not issue: + raise HTTPException(status_code=404, detail="Issue not found") + + return { + "id": issue.id, + "category": issue.category, + "description": issue.description, + "created_at": issue.created_at, + "image_path": issue.image_path, + "status": issue.status, + "upvotes": issue.upvotes or 0, + "location": issue.location, + "latitude": issue.latitude, + "longitude": issue.longitude, + "action_plan": issue.action_plan + } diff --git a/frontend/src/api/issues.js b/frontend/src/api/issues.js index 6683b58a..b6fa5baa 100644 --- a/frontend/src/api/issues.js +++ b/frontend/src/api/issues.js @@ -20,5 +20,13 @@ export const issuesApi = { vote: async (id) => { return await apiClient.post(`/api/issues/${id}/vote`, {}); // The backend endpoint might not require a body for upvote + }, + + getById: async (id) => { + return await apiClient.get(`/api/issues/${id}`); + }, + + verifyBlockchain: async (id) => { + return await apiClient.get(`/api/issues/${id}/blockchain-verify`); } }; diff --git a/frontend/src/views/VerifyView.jsx b/frontend/src/views/VerifyView.jsx index a1b0963d..84fd860f 100644 --- a/frontend/src/views/VerifyView.jsx +++ b/frontend/src/views/VerifyView.jsx @@ -1,6 +1,6 @@ import React, { useState, useEffect } from 'react'; import { useParams, useNavigate } from 'react-router-dom'; -import { Camera, Upload, CheckCircle, XCircle, AlertTriangle, ArrowLeft } from 'lucide-react'; +import { Camera, Upload, CheckCircle, XCircle, AlertTriangle, ArrowLeft, ShieldCheck } from 'lucide-react'; import { issuesApi } from '../api/issues'; const API_URL = import.meta.env.VITE_API_URL || ''; @@ -14,16 +14,18 @@ const VerifyView = () => { const [image, setImage] = useState(null); const [result, setResult] = useState(null); const [error, setError] = useState(null); + const [blockchainLoading, setBlockchainLoading] = useState(false); + const [blockchainResult, setBlockchainResult] = useState(null); useEffect(() => { const fetchIssue = async () => { try { - const issues = await issuesApi.getRecent(); - const found = issues.find(i => i.id === parseInt(id)); - if (found) { - setIssue(found); + // Optimization: Fetch issue by ID directly (O(1)) instead of searching recent list (O(N)) + const data = await issuesApi.getById(id); + if (data) { + setIssue(data); } else { - setError("Issue not found in recent list."); + setError("Issue not found."); } } catch (err) { console.error("Load failed", err); @@ -42,6 +44,23 @@ const VerifyView = () => { } }; + const handleBlockchainVerify = async () => { + setBlockchainLoading(true); + setBlockchainResult(null); + try { + const data = await issuesApi.verifyBlockchain(id); + setBlockchainResult(data); + } catch (err) { + console.error("Blockchain verification failed", err); + setBlockchainResult({ + is_valid: false, + message: "Failed to connect to the blockchain verification service." + }); + } finally { + setBlockchainLoading(false); + } + }; + const handleVerify = async () => { if (!image) return; setVerifying(true); @@ -141,6 +160,48 @@ const VerifyView = () => { )} + + {/* Blockchain Integrity Seal Section */} +
+ Every report in our system is cryptographically sealed and linked to the previous report, creating an immutable chain of records. Verify that this report hasn't been tampered with. +
+ + + + {blockchainResult && ( +{blockchainResult.is_valid ? 'Integrity Verified' : 'Integrity Compromised'}
+{blockchainResult.message}
+ {blockchainResult.current_hash && ( +Current Hash:
+ {blockchainResult.current_hash} +