""" Initialize blockchain with existing elections from database. This module ensures that when the backend starts, all elections in the database are recorded to the blockchain if they aren't already. """ import logging from sqlalchemy.orm import Session from . import models from .blockchain_elections import elections_blockchain, record_election_to_blockchain from datetime import datetime import json logger = logging.getLogger(__name__) def initialize_elections_blockchain(db: Session) -> None: """ Initialize the elections blockchain with all elections from database. Called on backend startup to ensure all elections are immutably recorded. Uses the elections_blockchain.blocks to check if an election is already recorded. Args: db: Database session """ logger.info("-" * 60) logger.info("Blockchain Initialization Started") logger.info("-" * 60) try: # Get all elections from database elections = db.query(models.Election).all() logger.info(f"Found {len(elections)} elections in database") if not elections: logger.warning("No elections to record to blockchain") return # Check which elections are already on blockchain existing_election_ids = {block.election_id for block in elections_blockchain.blocks} logger.info(f"Blockchain currently has {len(existing_election_ids)} elections") # Record each election that isn't already on blockchain recorded_count = 0 skipped_count = 0 for election in elections: if election.id in existing_election_ids: logger.debug(f" ⊘ Election {election.id} ({election.name}) already on blockchain") skipped_count += 1 continue try: # Get candidates for this election candidates = db.query(models.Candidate).filter( models.Candidate.election_id == election.id ).all() logger.debug(f" Recording election {election.id} with {len(candidates)} candidates") candidates_data = [ { "id": c.id, "name": c.name, "description": c.description or "", "order": c.order or 0 } for c in candidates ] # Record to blockchain block = record_election_to_blockchain( election_id=election.id, election_name=election.name, election_description=election.description or "", candidates=candidates_data, start_date=election.start_date.isoformat(), end_date=election.end_date.isoformat(), is_active=election.is_active, creator_id=0 # Database creation, no specific admin ) logger.info( f" ✓ Recorded election {election.id} ({election.name})\n" f" Block #{block.index}, Hash: {block.block_hash[:16]}..., " f"Candidates: {block.candidates_count}" ) recorded_count += 1 except Exception as e: logger.error( f" ✗ Failed to record election {election.id} ({election.name}): {e}", exc_info=True ) logger.info(f"Recording summary: {recorded_count} new, {skipped_count} skipped") # Verify blockchain integrity logger.info(f"Verifying blockchain integrity ({len(elections_blockchain.blocks)} blocks)...") if elections_blockchain.verify_chain_integrity(): logger.info(f"✓ Blockchain integrity verified successfully") else: logger.error("✗ Blockchain integrity check FAILED - possible corruption!") logger.info("-" * 60) logger.info(f"Blockchain Initialization Complete") logger.info(f" Total blocks: {len(elections_blockchain.blocks)}") logger.info(f" Chain valid: {elections_blockchain.verify_chain_integrity()}") logger.info("-" * 60) except Exception as e: logger.error( f"Blockchain initialization failed with error: {e}", exc_info=True ) raise