From 99d45ffe88bd52d231c0f2e9f07ed32fd0563ee1 Mon Sep 17 00:00:00 2001 From: vigneshs Date: Sun, 6 Oct 2024 01:52:41 +0530 Subject: [PATCH 1/4] Add Register, Login, Vote and Results page - Add DB --- src/app.py | 166 ++++++++++++++++++++++++------------ src/requirements.txt | Bin 1088 -> 1212 bytes src/static/style.css | 149 ++++++++++++++++++++++++++++---- src/templates/login.html | 24 ++++++ src/templates/register.html | 24 ++++++ src/templates/results.html | 41 +++++++++ src/templates/vote.html | 26 ++++++ 7 files changed, 358 insertions(+), 72 deletions(-) create mode 100644 src/templates/login.html create mode 100644 src/templates/register.html create mode 100644 src/templates/results.html create mode 100644 src/templates/vote.html diff --git a/src/app.py b/src/app.py index 714891c..4c205bc 100644 --- a/src/app.py +++ b/src/app.py @@ -1,62 +1,116 @@ -from flask import Flask, render_template, request, jsonify -import numpy as np -from qiskit import QuantumCircuit -from qiskit_aer import Aer -from qiskit.visualization import plot_histogram -import matplotlib.pyplot as plt -import io -import base64 - -# Flask app initialization +from flask import Flask, render_template, request, redirect, url_for, session, jsonify +from werkzeug.security import generate_password_hash, check_password_hash +import sqlite3 + app = Flask(__name__) +app.secret_key = 'your_secret_key' -# Homepage route -@app.route('/') -def index(): - """ - Route for the homepage. Renders the index.html template. - """ - return render_template('index.html') +# Create a database connection +def get_db_connection(): + conn = sqlite3.connect('votes.db') + conn.row_factory = sqlite3.Row + return conn -# Voting route to trigger quantum voting simulation -@app.route('/vote', methods=['POST']) -def vote(): - """ - Route to simulate a quantum voting system based on user input votes for each candidate. +# Create tables if they don't exist +def create_tables(): + conn = get_db_connection() + conn.execute('''CREATE TABLE IF NOT EXISTS users ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + username TEXT NOT NULL UNIQUE, + password TEXT NOT NULL)''') + + conn.execute('''CREATE TABLE IF NOT EXISTS votes ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + candidate INTEGER NOT NULL)''') + conn.commit() + conn.close() + +create_tables() + +# Registration route +@app.route('/register', methods=['GET', 'POST']) +def register(): + if request.method == 'POST': + username = request.form['username'] + password = request.form['password'] + hashed_password = generate_password_hash(password, method='pbkdf2:sha256', salt_length=16) + + conn = get_db_connection() + try: + conn.execute('INSERT INTO users (username, password) VALUES (?, ?)', (username, hashed_password)) + conn.commit() + conn.close() + return redirect(url_for('login')) + except sqlite3.IntegrityError: + conn.close() + return "Username already taken. Please try a different one." - Returns: - JSON response containing: - - vote_counts: A dictionary with the counts of each vote outcome. - - winner: The winner candidate based on the highest vote count. - - votes: The total number of votes received by the winner. - - image: Base64-encoded image of the vote distribution histogram. - """ - votes = request.json.get('votes', [0, 0, 0, 0]) # Get votes from the request - NUM_CANDIDATES = 4 # Number of candidates - - # Use the user-provided votes to simulate the quantum voting system - vote_counts = {'00': votes[0], '01': votes[1], '10': votes[2], '11': votes[3]} - - # Determine the winner by finding the candidate with the highest vote count - winner = max(vote_counts, key=vote_counts.get) - winner_candidate = int(winner, 2) # Convert binary string to integer (candidate number) - - # Plot the histogram of the vote counts and encode it as a base64 image - fig = plt.figure() - plot_histogram(vote_counts) # Plot the voting results as a histogram - buf = io.BytesIO() # Create an in-memory byte stream for the image - plt.savefig(buf, format='png') # Save the plot as a PNG image to the byte stream - buf.seek(0) - img_base64 = base64.b64encode(buf.getvalue()).decode() # Convert the image to base64 format - - # Return the vote counts, winner information, and histogram image as a JSON response - return jsonify({ - 'vote_counts': vote_counts, - 'winner': winner_candidate, - 'votes': vote_counts[winner], - 'image': img_base64 - }) - -# Main function to start the Flask application + return render_template('register.html') + +# Login route +@app.route('/login', methods=['GET', 'POST']) +def login(): + if request.method == 'POST': + username = request.form['username'] + password = request.form['password'] + + conn = get_db_connection() + user = conn.execute('SELECT * FROM users WHERE username = ?', (username,)).fetchone() + conn.close() + + if user and check_password_hash(user['password'], password): + session['username'] = username + return redirect(url_for('vote')) + else: + return "Invalid credentials. Please try again." + + return render_template('login.html') + +# Voting page +@app.route('/vote', methods=['GET', 'POST']) +def vote(): + if 'username' not in session: + return redirect(url_for('login')) + + if request.method == 'POST': + candidate = request.form['candidate'] # Get the selected candidate from the form + + # Insert the vote into the database + conn = get_db_connection() + conn.execute('INSERT INTO votes (candidate) VALUES (?)', (candidate,)) + conn.commit() + conn.close() + + # After vote submission, redirect to results page + return redirect(url_for('results')) + + return render_template('vote.html') + +# Results page to show current vote counts and winner +@app.route('/results') +def results(): + conn = get_db_connection() + + # Get the total votes for each candidate + vote_counts = conn.execute('SELECT candidate, COUNT(*) as count FROM votes GROUP BY candidate').fetchall() + + conn.close() + + # Find the candidate(s) with the highest vote count + if vote_counts: + max_votes = max([row['count'] for row in vote_counts]) + winners = [row['candidate'] for row in vote_counts if row['count'] == max_votes] + else: + max_votes = 0 + winners = [] + + return render_template('results.html', vote_counts=vote_counts, winners=winners, max_votes=max_votes) + +# Logout route +@app.route('/logout') +def logout(): + session.pop('username', None) + return redirect(url_for('login')) + if __name__ == '__main__': app.run(debug=True, host='0.0.0.0') diff --git a/src/requirements.txt b/src/requirements.txt index d5f0d3e1358d87524a48e61acda47fb8aab5db68..294b16b54daa78bd8a9578cdd1e6eb596d7e2a09 100644 GIT binary patch delta 125 zcmX@Wv4?ZQEnPQ;9EL=OVuox6U4~$WKn5QMN1#YDLk2@CLoP!lgDnsmGw3lGg7Czv q+LGxEML?-khCHCMRE83Sf{ouRnUrxEVFc7}0K~=&rjxUouL1z2&KcYQ delta 19 bcmdnPd4OZWt;sx$YMVnC3z#OqVZHzWOH~Jo diff --git a/src/static/style.css b/src/static/style.css index 09a27ea..48d735f 100644 --- a/src/static/style.css +++ b/src/static/style.css @@ -1,33 +1,150 @@ +/* General styles */ body { - background-color: #f0f2f5; - font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + font-family: Arial, sans-serif; + background-color: #f4f4f4; + margin: 0; + padding: 0; } h1 { - font-size: 2.5rem; + text-align: center; + color: #333; +} + +.container { + max-width: 600px; + margin: 50px auto; + background-color: #fff; + padding: 20px; + border-radius: 8px; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); +} + +/* Form styles */ +.form-vote { + display: flex; + flex-direction: column; + align-items: center; +} + +.candidate-select { + padding: 10px; + width: 100%; + font-size: 16px; + border-radius: 4px; + border: 1px solid #ccc; + margin-bottom: 20px; +} + +.btn-submit { + padding: 10px 20px; + background-color: #007bff; + color: #fff; + border: none; + border-radius: 4px; + cursor: pointer; + font-size: 16px; + transition: background-color 0.3s; +} + +.btn-submit:hover { + background-color: #0056b3; +} + +/* Table styles */ +.vote-results { + width: 100%; + border-collapse: collapse; + margin-top: 20px; +} + +.vote-results th, .vote-results td { + border: 1px solid #ddd; + padding: 8px; + text-align: center; +} + +.vote-results th { + background-color: #007bff; + color: white; +} + +.vote-results tr:nth-child(even) { + background-color: #f2f2f2; +} + +.vote-results tr:hover { + background-color: #ddd; +} + +/* Winner list styles */ +.winners-list { + list-style: none; + padding-left: 0; + text-align: center; +} + +.winners-list li { + font-size: 18px; font-weight: bold; - color: #007bff; + color: #28a745; } -h2, h3 { - color: #333; +/* Button styles */ +.btn-logout { + display: block; + text-align: center; + margin: 20px auto; + padding: 10px 20px; + background-color: #dc3545; + color: #fff; + text-decoration: none; + border-radius: 4px; + font-size: 16px; } -button { - font-size: 1.2rem; +.btn-logout:hover { + background-color: #c82333; +} + +/* Authentication form styles */ +.form-auth { + display: flex; + flex-direction: column; +} + +.input-field { + padding: 10px; + font-size: 16px; + border-radius: 4px; + border: 1px solid #ccc; + margin-bottom: 20px; +} + +.btn-submit { padding: 10px 20px; - margin: 10px; + background-color: #007bff; + color: #fff; + border: none; + border-radius: 4px; + cursor: pointer; + font-size: 16px; + transition: background-color 0.3s; } -#results { - margin-top: 30px; +.btn-submit:hover { + background-color: #0056b3; } -.card { - border-radius: 10px; +p { + text-align: center; } -#histogram { - max-width: 100%; - margin-top: 20px; +a { + color: #007bff; + text-decoration: none; } + +a:hover { + text-decoration: underline; +} \ No newline at end of file diff --git a/src/templates/login.html b/src/templates/login.html new file mode 100644 index 0000000..be211a8 --- /dev/null +++ b/src/templates/login.html @@ -0,0 +1,24 @@ + + + + + + Login - Q-Vote + + + +
+

Login

+
+ + + + + + + +
+

Don't have an account? Register here.

+
+ + diff --git a/src/templates/register.html b/src/templates/register.html new file mode 100644 index 0000000..7a35bd5 --- /dev/null +++ b/src/templates/register.html @@ -0,0 +1,24 @@ + + + + + + Register - Q-Vote + + + +
+

Register

+
+ + + + + + + +
+

Already have an account? Login here.

+
+ + diff --git a/src/templates/results.html b/src/templates/results.html new file mode 100644 index 0000000..5910830 --- /dev/null +++ b/src/templates/results.html @@ -0,0 +1,41 @@ + + + + + + + Voting Results + + + +
+

Results

+ + + + + + + + + + {% for row in vote_counts %} + + + + + {% endfor %} + +
CandidateTotal Votes
Candidate {{ row.candidate }}{{ row.count }}
+ +

Winner(s):

+ + + Logout +
+ + diff --git a/src/templates/vote.html b/src/templates/vote.html new file mode 100644 index 0000000..e2fd8ff --- /dev/null +++ b/src/templates/vote.html @@ -0,0 +1,26 @@ + + + + + + + Vote + + + +
+

Vote for Your Preferred Candidate

+
+ + +

+ +
+
+ + From 7e961a51a60dc210c5444dddd217dc74112e5661 Mon Sep 17 00:00:00 2001 From: vigneshs Date: Sun, 6 Oct 2024 01:59:30 +0530 Subject: [PATCH 2/4] Update .gitignore file --- .gitignore | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.gitignore b/.gitignore index 20c98cf..83f8588 100644 --- a/.gitignore +++ b/.gitignore @@ -59,3 +59,8 @@ Thumbs.db # Environment variables .env + +# Database file +/instance +votes.db # Ignore the votes database file +*.db \ No newline at end of file From a4068658a5b250dcd9c7358ff0776001b4984c21 Mon Sep 17 00:00:00 2001 From: vigneshs Date: Mon, 7 Oct 2024 00:03:46 +0530 Subject: [PATCH 3/4] Create new logic for Quantum Voting --- src/app.py | 149 +++++++++++++++++++++++--- src/blockchain.py | 113 ++++++++++++++++++++ src/templates/index.html | 205 ++++-------------------------------- src/templates/register.html | 2 +- src/templates/vote.html | 13 ++- src/test.py | 162 ++++++++++++++++++++++++++++ 6 files changed, 443 insertions(+), 201 deletions(-) create mode 100644 src/test.py diff --git a/src/app.py b/src/app.py index 4c205bc..6771b91 100644 --- a/src/app.py +++ b/src/app.py @@ -1,10 +1,21 @@ from flask import Flask, render_template, request, redirect, url_for, session, jsonify from werkzeug.security import generate_password_hash, check_password_hash import sqlite3 +import numpy as np +from qiskit import QuantumCircuit +from qiskit_aer import Aer +from qiskit.visualization import plot_histogram +import matplotlib.pyplot as plt +import io +import base64 app = Flask(__name__) app.secret_key = 'your_secret_key' +# Constants for quantum voting +NUM_CANDIDATES = 4 # Number of candidates in the election +NUM_QUBITS = 2 # Number of qubits needed (2 qubits can represent 4 states, 00, 01, 10, 11) + # Create a database connection def get_db_connection(): conn = sqlite3.connect('votes.db') @@ -66,12 +77,14 @@ def login(): return render_template('login.html') -# Voting page +# Voting route where users can vote for a candidate @app.route('/vote', methods=['GET', 'POST']) def vote(): if 'username' not in session: return redirect(url_for('login')) + current_vote_counts = [] # To store current vote counts + if request.method == 'POST': candidate = request.form['candidate'] # Get the selected candidate from the form @@ -79,32 +92,128 @@ def vote(): conn = get_db_connection() conn.execute('INSERT INTO votes (candidate) VALUES (?)', (candidate,)) conn.commit() + + # Retrieve all votes from the database for debugging + current_vote_counts = conn.execute('SELECT candidate, COUNT(*) as count FROM votes GROUP BY candidate').fetchall() conn.close() + # Debug: Print out current vote counts from the database + print("Current Vote Counts after Voting:") + for row in current_vote_counts: + print(f"Candidate {row['candidate']}: {row['count']} votes") + # After vote submission, redirect to results page return redirect(url_for('results')) - return render_template('vote.html') + # If GET request, retrieve current vote counts to display on voting page + conn = get_db_connection() + current_vote_counts = conn.execute('SELECT candidate, COUNT(*) as count FROM votes GROUP BY candidate').fetchall() + conn.close() -# Results page to show current vote counts and winner + return render_template('vote.html', current_vote_counts=current_vote_counts) + +# Results page where the quantum voting simulation occurs @app.route('/results') def results(): conn = get_db_connection() - # Get the total votes for each candidate - vote_counts = conn.execute('SELECT candidate, COUNT(*) as count FROM votes GROUP BY candidate').fetchall() - + # Retrieve all votes from the database + vote_counts_from_db = conn.execute('SELECT candidate, COUNT(*) as count FROM votes GROUP BY candidate').fetchall() conn.close() + # Create a vote_distribution array based on user votes (initialize for 4 candidates) + vote_distribution = [0] * NUM_CANDIDATES # Ensuring the size matches NUM_CANDIDATES + for row in vote_counts_from_db: + candidate_index = int(row['candidate']) + + # Ensure the candidate index is valid + if 0 <= candidate_index < NUM_CANDIDATES: + vote_distribution[candidate_index] = row['count'] + else: + print(f"Warning: Candidate index {candidate_index} is out of bounds.") + + # Simulate quantum voting with the retrieved vote distribution + return_data = quantum_voting(vote_distribution) + # Find the candidate(s) with the highest vote count - if vote_counts: - max_votes = max([row['count'] for row in vote_counts]) - winners = [row['candidate'] for row in vote_counts if row['count'] == max_votes] - else: - max_votes = 0 - winners = [] + winners = return_data['winner'] + if not isinstance(winners, list): + winners = [winners] # Ensure winners is a list, even if only one winner + + max_votes = return_data['votes'] - return render_template('results.html', vote_counts=vote_counts, winners=winners, max_votes=max_votes) + return render_template('results.html', vote_counts=vote_counts_from_db, winners=winners, max_votes=max_votes, image=return_data['image']) + +# Quantum voting function to simulate voting +def quantum_voting(vote_distribution): + """ + Simulates the quantum voting based on the provided vote distribution. + + Args: + vote_distribution (list): A list containing the count of votes for each candidate. + + Returns: + dict: A dictionary containing: + - vote_counts: A dictionary with the counts of each vote outcome. + - winner: The winner candidate based on the highest vote count. + - votes: The total number of votes received by the winner. + - image: Base64-encoded image of the vote distribution histogram. + """ + vote_counts = {'00': 0, '01': 0, '10': 0, '11': 0} # Store vote counts for each candidate (binary) + + for candidate_index in range(NUM_CANDIDATES): + candidate_vote_count = vote_distribution[candidate_index] # Get the vote count for this candidate + + for _ in range(candidate_vote_count): + # Create a new quantum circuit for each voter's vote + qc = QuantumCircuit(NUM_QUBITS, NUM_QUBITS) + + # Create a one-hot encoded vote vector (e.g., [0, 1, 0, 0] for vote 01) + vote_vector = [0] * NUM_CANDIDATES + vote_vector[candidate_index] = 1 + + # Encode the vote in the quantum circuit using amplitude encoding + amplitude_encoding(vote_vector, qc) + + # Apply Hadamard gate to create superposition and entangle qubits + qc.h(0) # Apply Hadamard gate on the first qubit + qc.cx(0, 1) # Entangle the first qubit with the second + + # Measure the qubits and store results in classical bits + qc.measure([0, 1], [0, 1]) + + # Use Aer's qasm_simulator to simulate the quantum circuit + backend = Aer.get_backend('qasm_simulator') + + # Run the circuit and simulate with 1 shot (single measurement per voter) + job = backend.run(qc, shots=1) + result = job.result() + counts = result.get_counts(qc) # Get measurement results + + # Update vote counts based on measurement outcomes + for outcome in counts: + if outcome in vote_counts: + vote_counts[outcome] += counts[outcome] + + # Determine winner(s) by finding candidates with highest count + max_vote_count = max(vote_counts.values()) + + winners = [int(k, 2) for k, v in vote_counts.items() if v == max_vote_count] + + # Plot histogram of results and encode it as base64 image + fig = plt.figure() + plot_histogram(vote_counts) + buf = io.BytesIO() + plt.savefig(buf, format='png') + buf.seek(0) + img_base64 = base64.b64encode(buf.getvalue()).decode() + + return { + 'vote_counts': vote_counts, + 'winner': winners, + 'votes': max_vote_count, + 'image': img_base64 + } # Logout route @app.route('/logout') @@ -112,5 +221,19 @@ def logout(): session.pop('username', None) return redirect(url_for('login')) +# Function to encode votes using amplitude encoding into a quantum circuit +def amplitude_encoding(vote_vec, quantum_circuit): + """ + Encodes a vote vector into a quantum circuit using amplitude encoding. + + Args: + vote_vec (list): A list representing a vote vector (one-hot encoded). + quantum_circuit (QuantumCircuit): A quantum circuit object to encode the vote. + """ + norm = np.linalg.norm(vote_vec) # Normalize the vote vector + normalized_vector = vote_vec / norm + quantum_circuit.initialize(normalized_vector, [0, 1]) # Initialize the circuit with the normalized vector + +# Main function to start the Flask application if __name__ == '__main__': app.run(debug=True, host='0.0.0.0') diff --git a/src/blockchain.py b/src/blockchain.py index e69de29..d7d7a54 100644 --- a/src/blockchain.py +++ b/src/blockchain.py @@ -0,0 +1,113 @@ +from flask import Flask, render_template, jsonify +import random +import numpy as np +from qiskit import QuantumCircuit +from qiskit_aer import Aer +from qiskit.visualization import plot_histogram +import matplotlib.pyplot as plt +import io +import base64 + +# Constants for quantum voting +NUM_CANDIDATES = 4 # Number of candidates in the election +NUM_QUBITS = 2 # Number of qubits needed (2 qubits can represent 4 states, 00, 01, 10, 11) +NUM_VOTERS = 4 # Number of voters participating in the voting simulation + +# Function to encode votes using amplitude encoding into a quantum circuit +def amplitude_encoding(vote_vec, quantum_circuit): + """ + Encodes a vote vector into a quantum circuit using amplitude encoding. + + Args: + vote_vec (list): A list representing a vote vector (one-hot encoded). + quantum_circuit (QuantumCircuit): A quantum circuit object to encode the vote. + """ + norm = np.linalg.norm(vote_vec) # Normalize the vote vector + normalized_vector = vote_vec / norm + quantum_circuit.initialize(normalized_vector, [0, 1]) # Initialize the circuit with the normalized vector + +# Flask app initialization +app = Flask(__name__) + +# Homepage route +@app.route('/') +def index(): + """ + Route for the homepage. Renders the index.html template. + """ + return render_template('index.html') + +# Voting route to trigger quantum voting simulation +@app.route('/vote') +def vote(): + """ + Route to simulate a quantum voting system. It simulates votes from multiple voters, + encodes them into quantum circuits, and then determines the winner based on the quantum measurement results. + + Returns: + JSON response containing: + - vote_counts: A dictionary with the counts of each vote outcome. + - winner: The winner candidate based on the highest vote count. + - votes: The total number of votes received by the winner. + - image: Base64-encoded image of the vote distribution histogram. + """ + vote_counts = {'00': 0, '01': 0, '10': 0, '11': 0} # Dictionary to store vote counts for each candidate (binary) + + for _ in range(NUM_VOTERS): + # Create a new quantum circuit for each voter with 2 qubits and 2 classical bits + qc = QuantumCircuit(NUM_QUBITS, NUM_QUBITS) + + # Generate a random vote for one of the 4 candidates (represented as binary 00, 01, 10, 11) + vote_choice = random.randint(0, NUM_CANDIDATES - 1) + + # Create a one-hot encoded vote vector (e.g., [0, 1, 0, 0] for vote 01) + vote_vector = [0] * NUM_CANDIDATES + vote_vector[vote_choice] = 1 + + # Encode the vote in the quantum circuit using amplitude encoding + amplitude_encoding(vote_vector, qc) + + # Apply Hadamard gate to create superposition and entangle qubits + qc.h(0) # Apply Hadamard gate on the first qubit + qc.cx(0, 1) # Entangle the first qubit with the second + + # Measure the qubits and store the results in classical bits + qc.measure([0, 1], [0, 1]) + + # Use Aer's qasm_simulator to simulate the quantum circuit + backend = Aer.get_backend('qasm_simulator') + + # Run the circuit and simulate with 1 shot (single vote measurement per voter) + job = backend.run(qc, shots=1) + result = job.result() + counts = result.get_counts(qc) # Get the result of the quantum measurement + + # Update vote counts based on the measurement outcomes + for outcome in counts: + vote_counts[outcome] += 1 + + print(vote_counts) + + # Determine the winner by finding the candidate with the highest vote count + winner = max(vote_counts, key=vote_counts.get) + winner_candidate = int(winner, 2) # Convert binary string to integer (candidate number) + + # Plot the histogram of the vote counts and encode it as a base64 image + fig = plt.figure() + plot_histogram(vote_counts) # Plot the voting results as a histogram + buf = io.BytesIO() # Create an in-memory byte stream for the image + plt.savefig(buf, format='png') # Save the plot as a PNG image to the byte stream + buf.seek(0) + img_base64 = base64.b64encode(buf.getvalue()).decode() # Convert the image to base64 format + + # Return the vote counts, winner information, and histogram image as a JSON response + return jsonify({ + 'vote_counts': vote_counts, + 'winner': winner_candidate, + 'votes': vote_counts[winner], + 'image': img_base64 + }) + +# Main function to start the Flask application +if __name__ == '__main__': + app.run(debug=True, host='0.0.0.0') \ No newline at end of file diff --git a/src/templates/index.html b/src/templates/index.html index 7531528..7373b36 100644 --- a/src/templates/index.html +++ b/src/templates/index.html @@ -1,134 +1,28 @@ - Quantum Voting System - + - - - - - - +
-
-
-

- Quantum Voting System -

- -
- -
- -
- -
-

Enter the Votes for each Candidate:

-
-
- - -
-
- - -
-
- - -
-
- - -
-
-
- -
+
+

+ Quantum Voting System +

+ +
+