├── .gitignore
├── LICENSE
├── README.md
├── ballot
├── __init__.py
├── admin.py
├── apps.py
├── migrations
│ └── __init__.py
├── models.py
├── static
│ └── ballot
│ │ └── img
│ │ ├── jovi.jpg
│ │ ├── mercury.jpg
│ │ └── suicidal.jpg
├── templates
│ └── ballot
│ │ ├── create.html
│ │ ├── seal.html
│ │ └── status.html
├── tests.py
├── urls.py
└── views.py
├── bbevoting_project
├── __init__.py
├── demo_private.pem
├── settings.py
├── urls.py
└── wsgi.py
├── manage.py
├── requirements.txt
├── screenshots
├── ballot.PNG
├── ballot_block_detail.PNG
├── ballot_sealing_process.PNG
├── ballot_verification.PNG
├── bl30.PNG
├── block.PNG
├── chained_blocks.PNG
├── homepage.PNG
├── tampering_data.PNG
├── transactions.PNG
└── tx_1000.PNG
├── simulation
├── __init__.py
├── admin.py
├── apps.py
├── merkle
│ ├── LICENSE
│ ├── __init__.py
│ └── merkle_tool.py
├── migrations
│ └── __init__.py
├── models.py
├── templates
│ └── simulation
│ │ ├── block.html
│ │ ├── blockchain.html
│ │ ├── generate.html
│ │ └── transactions.html
├── templatetags
│ ├── __init__.py
│ └── sim_filters.py
├── tests.py
├── urls.py
└── views.py
├── static
├── css
│ └── main.css
└── js
│ └── main.js
├── templates
└── base.html
└── welcome
├── __init__.py
├── admin.py
├── apps.py
├── migrations
└── __init__.py
├── models.py
├── templates
└── welcome
│ └── home.html
├── tests.py
├── urls.py
└── views.py
/.gitignore:
--------------------------------------------------------------------------------
1 | # This is the minimal gitignore for Django
2 |
3 | # Exclude the test database
4 | db.sqlite3
5 | __pycache__
6 | .vscode
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Gottfried Prasetyadi
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Blockchain-based E-voting Simulation
2 |
3 | ## Description
4 |
5 | This is a Python-based Django project to simulate a concept of blockchain-based e-voting protocol. This project should be run only on the development server with Debug mode on. The simulation comprises two sections: __"Block"__ and __"Chain"__.
6 |
7 | ### "Block"
8 |
9 | Scenario: a potential voter has to present himself to the voting organizer, showing his ID and other legal documents. Finally, he has to submit a public key i.e., ECC. Assume that he has successfully registered the public key.
10 |
11 | Via the ballot page (see screenshot below), the voter needs to enter his pseudonym (UUID 4) and candidate number. The ballot is then signed using a private key. If the submitted ballot is proven to be valid, it will be sealed (mined). In this section, one block contains only the aforementioned ballot. You can examine the whole process in detail using the console.
12 |
13 | (The public-private key pair is hard coded and generated externally. See Technical Details below.)
14 |
15 | 
16 |
17 | ### "Chain"
18 |
19 | In this section, transactions (ballots) with valid data will be generated. They are then sealed into blocks. After that, you can explore the transactions and blocks, try to tamper the records, and verify them.
20 |
21 | 
22 |
23 | 
24 |
25 | _The screenshot above shows that a node's database (i.e., your node) has been tampered. You will not see this in real blockchain explorer, but this should give you a glimpse of why tampering immutable ledger is futile._
26 |
27 | ## How to run
28 |
29 | 1. Get your virtual environment ready (recommended).
30 | 2. Locate the 'requirements.txt' file. Install necessary packages: `pip install -r requirements.txt`.
31 | 3. Locate the 'manage.py' file. Run `python manage.py runserver`. Then access http://localhost:8000.
32 | 4. From the homepage, either run 'Block' or 'Chain' section by clicking 'Start'.
33 |
34 | ## Technical Details
35 |
36 | The private key used in this demo is located in 'bbevoting_project' folder ('demo_private.pem' file), while the corresponding public key is hard coded in 'settings.py' (look for `PUBLIC_KEY`). You may also set some config vars such as `N_TRANSACTIONS`, `N_TX_PER_BLOCK`, and the puzzle difficulty (`PUZZLE` and `PLENGTH`).
37 |
38 | ## Screenshots
39 |
40 | See the 'screenshots' folder.
41 |
42 | ## Acknowledgement
43 |
44 | For Prof. ABM.
45 |
46 | This project uses a modified version of "pymerkletools" by Tierion for creating merkle root using SHA3.
47 |
48 | ## License
49 |
50 | See included MIT License.
51 |
--------------------------------------------------------------------------------
/ballot/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GottfriedCP/Blockchain-based-E-Voting-Simulation/148935558f5888779d3d4846779f3547c781fae0/ballot/__init__.py
--------------------------------------------------------------------------------
/ballot/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 |
3 | # Register your models here.
4 |
--------------------------------------------------------------------------------
/ballot/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class BallotConfig(AppConfig):
5 | name = 'ballot'
6 |
--------------------------------------------------------------------------------
/ballot/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GottfriedCP/Blockchain-based-E-Voting-Simulation/148935558f5888779d3d4846779f3547c781fae0/ballot/migrations/__init__.py
--------------------------------------------------------------------------------
/ballot/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 |
--------------------------------------------------------------------------------
/ballot/static/ballot/img/jovi.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GottfriedCP/Blockchain-based-E-Voting-Simulation/148935558f5888779d3d4846779f3547c781fae0/ballot/static/ballot/img/jovi.jpg
--------------------------------------------------------------------------------
/ballot/static/ballot/img/mercury.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GottfriedCP/Blockchain-based-E-Voting-Simulation/148935558f5888779d3d4846779f3547c781fae0/ballot/static/ballot/img/mercury.jpg
--------------------------------------------------------------------------------
/ballot/static/ballot/img/suicidal.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GottfriedCP/Blockchain-based-E-Voting-Simulation/148935558f5888779d3d4846779f3547c781fae0/ballot/static/ballot/img/suicidal.jpg
--------------------------------------------------------------------------------
/ballot/templates/ballot/create.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 | {% load static %}
3 | {% block title_bl %}Create ballot{% endblock %}
4 | {% block body_bl %}
5 |
6 |
Best Artist Nomination
7 |
43 |
44 | {% endblock %}
--------------------------------------------------------------------------------
/ballot/templates/ballot/seal.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 | {% load static %}
3 | {% load sim_filters %}
4 | {% block title_bl %}Block detail{% endblock %}
5 |
6 | {% block body_bl %}
7 |
8 |
Block detail
9 |
10 |
11 | Previous hash
12 | {{ prev_hash }}
13 |
14 | Transaction hash
15 | {{ transaction_hash }}
16 |
17 | Block hash
18 | {{ block_hash }}
19 |
20 | Nonce
21 | {{ nonce }}
22 |
23 | Timestamp
24 | {{ timestamp }} ({{ timestamp|unix_to_date|date:"Y-m-d H:i:s" }})
25 |
26 |
Back to homepage
27 |
28 |
29 | {% endblock %}
--------------------------------------------------------------------------------
/ballot/templates/ballot/status.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 | {% block title_bl %}Status{% endblock %}
3 |
4 | {% block body_bl %}
5 |
6 |
Signature Verification
7 |
8 |
Your ballot was {{ ballot }}
9 |
Generated signature {{ signature }}
10 |
11 | {{ status }}
12 |
13 |
14 | {% if not error %}
15 |
16 | {% csrf_token %}
17 |
18 | Seal the ballot
19 |
20 | {% endif %}
21 |
22 |
23 |
24 | {% endblock %}
--------------------------------------------------------------------------------
/ballot/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 |
3 | # Create your tests here.
4 |
--------------------------------------------------------------------------------
/ballot/urls.py:
--------------------------------------------------------------------------------
1 | from django.urls import path
2 | from . import views
3 |
4 | app_name = 'ballot'
5 | urlpatterns = [
6 | path('create/', views.create, name='create'),
7 | path('seal/', views.seal, name='seal'),
8 | ]
9 |
--------------------------------------------------------------------------------
/ballot/views.py:
--------------------------------------------------------------------------------
1 | import time, datetime
2 | import uuid
3 | from Crypto.Signature import DSS
4 | from Crypto.Hash import SHA3_256
5 | from Crypto.PublicKey import ECC
6 | from Crypto import Random
7 | from django.conf import settings
8 | from django.http import HttpResponse
9 | from django.shortcuts import redirect, render
10 |
11 | def create(request):
12 | if request.method == 'POST':
13 | voter_id = request.POST.get('voter-id-input')
14 | vote = request.POST.get('vote-input')
15 | private_key = request.POST.get('private-key-input')
16 |
17 | # Create ballot as string vector
18 | timestamp = datetime.datetime.now().timestamp()
19 | ballot = "{}|{}|{}".format(voter_id, vote, timestamp)
20 | print('\ncasted ballot: {}\n'.format(ballot))
21 | signature = ''
22 | try:
23 | # Create signature
24 | priv_key = ECC.import_key(private_key)
25 | h = SHA3_256.new(ballot.encode('utf-8'))
26 | signature = DSS.new(priv_key, 'fips-186-3').sign(h)
27 | print('\nsignature: {}\n'.format(signature.hex()))
28 |
29 | # Verify the signature using registered public key
30 | pub_key = ECC.import_key(settings.PUBLIC_KEY)
31 | verifier = DSS.new(pub_key, 'fips-186-3')
32 |
33 | verifier.verify(h, signature)
34 | status = 'The ballot is signed successfully.'
35 | error = False
36 | except (ValueError, TypeError):
37 | status = 'The key is not registered.'
38 | error = True
39 |
40 | context = {
41 | 'ballot': ballot,
42 | 'signature': signature,
43 | 'status': status,
44 | 'error': error,
45 | }
46 | return render(request, 'ballot/status.html', context)
47 |
48 | context = {'voter_id': uuid.uuid4(), }
49 | return render(request, 'ballot/create.html', context)
50 |
51 | def seal(request):
52 | if request.method == 'POST':
53 | ballot = request.POST.get('ballot_input')
54 | ballot_byte = ballot.encode('utf-8')
55 | ballot_hash = SHA3_256.new(ballot_byte).hexdigest()
56 | # Puzzle requirement: '0' * n (n leading zeros)
57 | puzzle, pcount = settings.PUZZLE, settings.PLENGTH
58 | nonce = 0
59 |
60 | # Try to solve puzzle
61 | start_time = time.time() # benchmark
62 | timestamp = datetime.datetime.now().timestamp() # mark the start of mining effort
63 | while True:
64 | block_hash = SHA3_256.new(("{}{}{}".format(ballot, nonce, timestamp).encode('utf-8'))).hexdigest()
65 | print('\ntrial hash: {}\n'.format(block_hash))
66 | if block_hash[:pcount] == puzzle:
67 | stop_time = time.time() # benchmark
68 | print("\nblock is sealed in {} seconds\n".format(stop_time-start_time))
69 | break
70 | nonce += 1
71 |
72 | context = {
73 | 'prev_hash': 'GENESIS',
74 | 'transaction_hash': ballot_hash,
75 | 'nonce': nonce,
76 | 'block_hash': block_hash,
77 | 'timestamp': timestamp,
78 | }
79 | return render(request, 'ballot/seal.html', context)
80 | return redirect('ballot:create')
81 |
--------------------------------------------------------------------------------
/bbevoting_project/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GottfriedCP/Blockchain-based-E-Voting-Simulation/148935558f5888779d3d4846779f3547c781fae0/bbevoting_project/__init__.py
--------------------------------------------------------------------------------
/bbevoting_project/demo_private.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN PRIVATE KEY-----
2 | MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgkScFYOR4uBsYXmd5
3 | WHCY+NVm14uBz7QbYu532ropdsShRANCAAQnmv4Jpv6kOMmbUg25YMkV9M9y+omU
4 | gQ80v+AVHLP2yXnk3yMQ2YKyU5Kk0KQlQRvzv3H7QnHSmD08544BPtev
5 | -----END PRIVATE KEY-----
--------------------------------------------------------------------------------
/bbevoting_project/settings.py:
--------------------------------------------------------------------------------
1 | """
2 | Django settings for bbevoting_project project.
3 |
4 | Generated by 'django-admin startproject' using Django 2.1.2.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/2.1/topics/settings/
8 |
9 | For the full list of settings and their values, see
10 | https://docs.djangoproject.com/en/2.1/ref/settings/
11 | """
12 |
13 | import os, math
14 |
15 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
16 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
17 |
18 |
19 | # Quick-start development settings - unsuitable for production
20 | # See https://docs.djangoproject.com/en/2.1/howto/deployment/checklist/
21 |
22 | # SECURITY WARNING: keep the secret key used in production secret!
23 | SECRET_KEY = '9%k^*0(qi*oktfa+#a3pp9g3eo-9vd(kws8+$t#lf2u&b#+u-*'
24 |
25 | # SECURITY WARNING: don't run with debug turned on in production!
26 | DEBUG = True
27 |
28 | ALLOWED_HOSTS = ['localhost', '127.0.0.1', ]
29 |
30 |
31 | # Application definition
32 |
33 | INSTALLED_APPS = [
34 | 'django.contrib.admin',
35 | 'django.contrib.auth',
36 | 'django.contrib.contenttypes',
37 | 'django.contrib.sessions',
38 | 'django.contrib.messages',
39 | 'django.contrib.staticfiles',
40 | 'ballot',
41 | 'simulation',
42 | 'welcome',
43 | ]
44 |
45 | MIDDLEWARE = [
46 | 'django.middleware.security.SecurityMiddleware',
47 | 'django.contrib.sessions.middleware.SessionMiddleware',
48 | 'django.middleware.common.CommonMiddleware',
49 | 'django.middleware.csrf.CsrfViewMiddleware',
50 | 'django.contrib.auth.middleware.AuthenticationMiddleware',
51 | 'django.contrib.messages.middleware.MessageMiddleware',
52 | 'django.middleware.clickjacking.XFrameOptionsMiddleware',
53 | ]
54 |
55 | ROOT_URLCONF = 'bbevoting_project.urls'
56 |
57 | TEMPLATES = [
58 | {
59 | 'BACKEND': 'django.template.backends.django.DjangoTemplates',
60 | 'DIRS': ['templates', ],
61 | 'APP_DIRS': True,
62 | 'OPTIONS': {
63 | 'context_processors': [
64 | 'django.template.context_processors.debug',
65 | 'django.template.context_processors.request',
66 | 'django.contrib.auth.context_processors.auth',
67 | 'django.contrib.messages.context_processors.messages',
68 | ],
69 | },
70 | },
71 | ]
72 |
73 | WSGI_APPLICATION = 'bbevoting_project.wsgi.application'
74 |
75 |
76 | # Database
77 | # https://docs.djangoproject.com/en/2.1/ref/settings/#databases
78 |
79 | DATABASES = {
80 | 'default': {
81 | 'ENGINE': 'django.db.backends.sqlite3',
82 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
83 | }
84 | }
85 |
86 |
87 | # Password validation
88 | # https://docs.djangoproject.com/en/2.1/ref/settings/#auth-password-validators
89 |
90 | AUTH_PASSWORD_VALIDATORS = [
91 | {
92 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
93 | },
94 | {
95 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
96 | },
97 | {
98 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
99 | },
100 | {
101 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
102 | },
103 | ]
104 |
105 |
106 | # Internationalization
107 | # https://docs.djangoproject.com/en/2.1/topics/i18n/
108 |
109 | LANGUAGE_CODE = 'en-us'
110 |
111 | TIME_ZONE = 'Asia/Jakarta'
112 |
113 | USE_I18N = True
114 |
115 | USE_L10N = True
116 |
117 | USE_TZ = True
118 |
119 |
120 | # Static files (CSS, JavaScript, Images)
121 | # https://docs.djangoproject.com/en/2.1/howto/static-files/
122 |
123 | STATIC_URL = '/static/'
124 |
125 | STATICFILES_DIRS = [
126 | os.path.join(BASE_DIR, 'static'),
127 | ]
128 |
129 | # FOR DEMO PURPOSE
130 | # IMPORTANT: Re-run the simulation after modifying these values.
131 | # Public key
132 | PUBLIC_KEY = """-----BEGIN PUBLIC KEY-----
133 | MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEJ5r+Cab+pDjJm1INuWDJFfTPcvqJ
134 | lIEPNL/gFRyz9sl55N8jENmCslOSpNCkJUEb879x+0Jx0pg9POeOAT7Xrw==
135 | -----END PUBLIC KEY-----"""
136 |
137 | # PUZZLE
138 | PUZZLE = '0000' # or more zeros
139 | PLENGTH = len(PUZZLE)
140 |
141 | # BATCH SIMULATION
142 | N_TRANSACTIONS = 10000
143 | N_TX_PER_BLOCK = 20
144 | N_BLOCKS = math.ceil(N_TRANSACTIONS/N_TX_PER_BLOCK) # Number of required blocks as int
145 |
--------------------------------------------------------------------------------
/bbevoting_project/urls.py:
--------------------------------------------------------------------------------
1 | """bbevoting_project URL Configuration
2 |
3 | The `urlpatterns` list routes URLs to views. For more information please see:
4 | https://docs.djangoproject.com/en/2.1/topics/http/urls/
5 | Examples:
6 | Function views
7 | 1. Add an import: from my_app import views
8 | 2. Add a URL to urlpatterns: path('', views.home, name='home')
9 | Class-based views
10 | 1. Add an import: from other_app.views import Home
11 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
12 | Including another URLconf
13 | 1. Import the include() function: from django.urls import include, path
14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
15 | """
16 | from django.contrib import admin
17 | from django.urls import include, path
18 | from django.conf import settings
19 | from django.conf.urls.static import static
20 |
21 | urlpatterns = [
22 | path('admin/', admin.site.urls),
23 |
24 | path('ballot/', include('ballot.urls')),
25 | path('sim/', include('simulation.urls')),
26 | path('', include('welcome.urls')),
27 | ] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
28 |
--------------------------------------------------------------------------------
/bbevoting_project/wsgi.py:
--------------------------------------------------------------------------------
1 | """
2 | WSGI config for bbevoting_project project.
3 |
4 | It exposes the WSGI callable as a module-level variable named ``application``.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/2.1/howto/deployment/wsgi/
8 | """
9 |
10 | import os
11 |
12 | from django.core.wsgi import get_wsgi_application
13 |
14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'bbevoting_project.settings')
15 |
16 | application = get_wsgi_application()
17 |
--------------------------------------------------------------------------------
/manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | import os
3 | import sys
4 |
5 | if __name__ == '__main__':
6 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'bbevoting_project.settings')
7 | try:
8 | from django.core.management import execute_from_command_line
9 | except ImportError as exc:
10 | raise ImportError(
11 | "Couldn't import Django. Are you sure it's installed and "
12 | "available on your PYTHONPATH environment variable? Did you "
13 | "forget to activate a virtual environment?"
14 | ) from exc
15 | execute_from_command_line(sys.argv)
16 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | Django==2.2.13
2 | pycryptodome==3.6.6
--------------------------------------------------------------------------------
/screenshots/ballot.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GottfriedCP/Blockchain-based-E-Voting-Simulation/148935558f5888779d3d4846779f3547c781fae0/screenshots/ballot.PNG
--------------------------------------------------------------------------------
/screenshots/ballot_block_detail.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GottfriedCP/Blockchain-based-E-Voting-Simulation/148935558f5888779d3d4846779f3547c781fae0/screenshots/ballot_block_detail.PNG
--------------------------------------------------------------------------------
/screenshots/ballot_sealing_process.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GottfriedCP/Blockchain-based-E-Voting-Simulation/148935558f5888779d3d4846779f3547c781fae0/screenshots/ballot_sealing_process.PNG
--------------------------------------------------------------------------------
/screenshots/ballot_verification.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GottfriedCP/Blockchain-based-E-Voting-Simulation/148935558f5888779d3d4846779f3547c781fae0/screenshots/ballot_verification.PNG
--------------------------------------------------------------------------------
/screenshots/bl30.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GottfriedCP/Blockchain-based-E-Voting-Simulation/148935558f5888779d3d4846779f3547c781fae0/screenshots/bl30.PNG
--------------------------------------------------------------------------------
/screenshots/block.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GottfriedCP/Blockchain-based-E-Voting-Simulation/148935558f5888779d3d4846779f3547c781fae0/screenshots/block.PNG
--------------------------------------------------------------------------------
/screenshots/chained_blocks.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GottfriedCP/Blockchain-based-E-Voting-Simulation/148935558f5888779d3d4846779f3547c781fae0/screenshots/chained_blocks.PNG
--------------------------------------------------------------------------------
/screenshots/homepage.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GottfriedCP/Blockchain-based-E-Voting-Simulation/148935558f5888779d3d4846779f3547c781fae0/screenshots/homepage.PNG
--------------------------------------------------------------------------------
/screenshots/tampering_data.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GottfriedCP/Blockchain-based-E-Voting-Simulation/148935558f5888779d3d4846779f3547c781fae0/screenshots/tampering_data.PNG
--------------------------------------------------------------------------------
/screenshots/transactions.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GottfriedCP/Blockchain-based-E-Voting-Simulation/148935558f5888779d3d4846779f3547c781fae0/screenshots/transactions.PNG
--------------------------------------------------------------------------------
/screenshots/tx_1000.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GottfriedCP/Blockchain-based-E-Voting-Simulation/148935558f5888779d3d4846779f3547c781fae0/screenshots/tx_1000.PNG
--------------------------------------------------------------------------------
/simulation/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GottfriedCP/Blockchain-based-E-Voting-Simulation/148935558f5888779d3d4846779f3547c781fae0/simulation/__init__.py
--------------------------------------------------------------------------------
/simulation/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 |
3 | # Register your models here.
4 |
--------------------------------------------------------------------------------
/simulation/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class SimulationConfig(AppConfig):
5 | name = 'simulation'
6 |
--------------------------------------------------------------------------------
/simulation/merkle/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Tierion
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/simulation/merkle/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GottfriedCP/Blockchain-based-E-Voting-Simulation/148935558f5888779d3d4846779f3547c781fae0/simulation/merkle/__init__.py
--------------------------------------------------------------------------------
/simulation/merkle/merkle_tool.py:
--------------------------------------------------------------------------------
1 | import hashlib
2 | from Crypto.Hash import SHA3_256
3 |
4 | class MerkleTools(object):
5 | def __init__(self):
6 | self.reset_tree()
7 |
8 | def _to_hex(self, x):
9 | return x.hex()
10 |
11 | def reset_tree(self):
12 | self.leaves = list()
13 | self.levels = None
14 | self.is_ready = False
15 |
16 | def add_leaf(self, values, do_hash=False):
17 | self.is_ready = False
18 | # check if single leaf
19 | if not isinstance(values, tuple) and not isinstance(values, list):
20 | values = [values]
21 | for v in values:
22 | if do_hash:
23 | v = v.encode('utf-8')
24 | v = SHA3_256.new(v).hexdigest()
25 | v = bytearray.fromhex(v)
26 | self.leaves.append(v)
27 |
28 | def get_leaf(self, index):
29 | return self._to_hex(self.leaves[index])
30 |
31 | def get_leaf_count(self):
32 | return len(self.leaves)
33 |
34 | def get_tree_ready_state(self):
35 | return self.is_ready
36 |
37 | def _calculate_next_level(self):
38 | solo_leave = None
39 | N = len(self.levels[0]) # number of leaves on the level
40 | if N % 2 == 1: # if odd number of leaves on the level
41 | solo_leave = self.levels[0][-1]
42 | N -= 1
43 |
44 | new_level = []
45 | for l, r in zip(self.levels[0][0:N:2], self.levels[0][1:N:2]):
46 | new_level.append(SHA3_256.new(l+r).digest())
47 | if solo_leave is not None:
48 | new_level.append(solo_leave)
49 | self.levels = [new_level, ] + self.levels # prepend new level
50 |
51 | def make_tree(self):
52 | self.is_ready = False
53 | if self.get_leaf_count() > 0:
54 | self.levels = [self.leaves, ]
55 | while len(self.levels[0]) > 1:
56 | self._calculate_next_level()
57 | self.is_ready = True
58 |
59 | def get_merkle_root(self):
60 | if self.is_ready:
61 | if self.levels is not None:
62 | return self._to_hex(self.levels[0][0])
63 | else:
64 | return None
65 | else:
66 | return None
67 |
68 | def get_proof(self, index):
69 | if self.levels is None:
70 | return None
71 | elif not self.is_ready or index > len(self.leaves)-1 or index < 0:
72 | return None
73 | else:
74 | proof = []
75 | for x in range(len(self.levels) - 1, 0, -1):
76 | level_len = len(self.levels[x])
77 | if (index == level_len - 1) and (level_len % 2 == 1): # skip if this is an odd end node
78 | index = int(index / 2.)
79 | continue
80 | is_right_node = index % 2
81 | sibling_index = index - 1 if is_right_node else index + 1
82 | sibling_pos = "left" if is_right_node else "right"
83 | sibling_value = self._to_hex(self.levels[x][sibling_index])
84 | proof.append({sibling_pos: sibling_value})
85 | index = int(index / 2.)
86 | return proof
87 |
88 | def validate_proof(self, proof, target_hash, merkle_root):
89 | merkle_root = bytearray.fromhex(merkle_root)
90 | target_hash = bytearray.fromhex(target_hash)
91 | if len(proof) == 0:
92 | return target_hash == merkle_root
93 | else:
94 | proof_hash = target_hash
95 | for p in proof:
96 | try:
97 | # the sibling is a left node
98 | sibling = bytearray.fromhex(p['left'])
99 | proof_hash = SHA3_256.new(sibling + proof_hash).digest()
100 | except:
101 | # the sibling is a right node
102 | sibling = bytearray.fromhex(p['right'])
103 | proof_hash = SHA3_256.new(proof_hash + sibling).digest()
104 | return proof_hash == merkle_root
105 |
--------------------------------------------------------------------------------
/simulation/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GottfriedCP/Blockchain-based-E-Voting-Simulation/148935558f5888779d3d4846779f3547c781fae0/simulation/migrations/__init__.py
--------------------------------------------------------------------------------
/simulation/models.py:
--------------------------------------------------------------------------------
1 | import datetime, uuid, random
2 | from django.db import models
3 |
4 | def get_vote():
5 | return random.randint(1, 3)
6 |
7 | def get_timestamp():
8 | return datetime.datetime.now().timestamp()
9 |
10 | class Vote(models.Model):
11 | id = models.UUIDField(primary_key=True, default=uuid.uuid4)
12 | vote = models.IntegerField(default=get_vote)
13 | timestamp = models.FloatField(default=get_timestamp)
14 | # Not ForeignKey! See transactions() in simulation.views for implications
15 | block_id = models.IntegerField(null=True)
16 |
17 | def __str__(self):
18 | return "{}|{}|{}".format(self.id, self.vote, self.timestamp)
19 |
20 | class Block(models.Model):
21 | prev_h = models.CharField(max_length=64, blank=True)
22 | merkle_h = models.CharField(max_length=64, blank=True)
23 | h = models.CharField(max_length=64, blank=True)
24 | nonce = models.IntegerField(null=True)
25 | timestamp = models.FloatField(default=get_timestamp)
26 |
27 | def __str__(self):
28 | return str(self.id)
29 |
30 | class VoteBackup(models.Model):
31 | """This model acts as backup; its objects shall never be tampered."""
32 | id = models.UUIDField(primary_key=True, default=uuid.uuid4)
33 | vote = models.IntegerField(default=get_vote)
34 | timestamp = models.FloatField(default=get_timestamp)
35 | block_id = models.IntegerField(null=True)
36 |
37 | def __str__(self):
38 | return "{}|{}|{}".format(self.id, self.vote, self.timestamp)
39 |
--------------------------------------------------------------------------------
/simulation/templates/simulation/block.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 | {% load static %}
3 | {% load sim_filters %}
4 | {% block title_bl %}Block #{{ bk.pk }}{% endblock %}
5 |
6 | {% block body_bl %}
7 |
8 |
Block #{{ bk.id }}
9 |
Back to blocks list
10 |
Back to homepage
11 |
12 |
13 | Previous hash
14 | {{ bk.prev_h }}
15 |
16 | Merkle root hash
17 | {{ bk.merkle_h }}
18 |
19 | Block hash
20 | {{ bk.h }}
21 |
22 | Nonce
23 | {{ bk.nonce }}
24 |
25 | Timestamp
26 | {{ bk.timestamp|unix_to_date|date:"Y-m-d H:i:s" }} ({{ bk.timestamp|unix_to_date|timesince }} ago)
27 |
28 | Confirmations
29 | {{ confirmed_by }} confirmations
30 |
31 |
32 |
33 | {% if prev_block %}
34 |
Previous block
35 | {% endif %}
36 | {% if next_block %}
37 |
Next block
38 | {% endif %}
39 |
40 |
41 |
Transactions
42 |
Re-calculated merkle root hash: {{ verified_merkle_h }}
43 | {% if tampered %}
44 |
45 |
Transactions are tampered Sync with other nodes
46 |
47 | {% else %}
48 |
49 |
Transactions are not tampered
50 |
51 | {% endif %}
52 |
53 |
Showing page {{ transactions_pg.number }} of {{ transactions_pg.paginator.num_pages }}
54 |
66 |
67 |
68 |
69 |
70 | #
71 | Voter ID
72 | Cand
73 | Timestamp
74 | Hash
75 |
76 |
77 |
78 | {% for t, h in transactions %}
79 |
80 | {{ forloop.counter }}
81 | {{ t.id }}
82 | {{ t.vote }}
83 | {{ t.timestamp|unix_to_date|date:"Y-m-d H:i:s" }}
84 | {{ h|truncatechars:32 }}
85 |
86 | {% endfor %}
87 |
88 |
89 |
90 |
91 | {% endblock %}
--------------------------------------------------------------------------------
/simulation/templates/simulation/blockchain.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 | {% load sim_filters %}
3 | {% block title_bl %}Blocks{% endblock %}
4 |
5 | {% block body_bl %}
6 |
7 |
List of blocks
8 |
See all transactions
9 |
Back to homepage
10 | {% if messages %}
11 | {% for message in messages %}
12 |
15 | {% endfor %}
16 | {% endif %}
17 |
18 |
19 |
20 | #
21 | Previous Hash
22 | Block Hash
23 | Merkle Root Hash
24 | Nonce
25 | Timestamp
26 |
27 |
28 |
29 | {% for block in blocks %}
30 |
31 | {{ block.id }}
32 | {{ block.prev_h|truncatechars:30 }}
33 | {{ block.h|truncatechars:30 }}
34 | {{ block.merkle_h|truncatechars:30 }}
35 | {{ block.nonce }}
36 | {{ block.timestamp|unix_to_date|date:"Y-m-d H:i:s" }}
37 |
38 | {% empty %}
39 | No record.
40 | {% endfor %}
41 |
42 |
43 |
Verify
44 |
Sync
45 |
46 | {% endblock %}
--------------------------------------------------------------------------------
/simulation/templates/simulation/generate.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 | {% block title_bl %}Transactions{% endblock %}
3 |
4 | {% block body_bl %}
5 |
6 |
List of unconfirmed votes
7 |
Showing last 100 transactions (or all if <=100)
8 |
9 |
10 |
11 |
12 | Voter ID
13 | Vote
14 | Timestamp
15 |
16 |
17 |
18 | {% for ballot in votes %}
19 |
20 | {{ ballot.id }}
21 | {{ ballot.vote }}
22 | {{ ballot.timestamp }}
23 |
24 | {% endfor %}
25 |
26 |
27 |
28 |
Mine into blocks
29 |
30 | {% endblock %}
--------------------------------------------------------------------------------
/simulation/templates/simulation/transactions.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 | {% load sim_filters %}
3 | {% block title_bl %}Transactions{% endblock %}
4 |
5 | {% block body_bl %}
6 |
7 |
List of confirmed votes
8 |
See all blocks
9 |
Back to homepage
10 |
11 | {% for r in result %}
12 | Candidate #{{ forloop.counter }}: {{ r }} votes
13 | {% endfor %}
14 |
15 |
16 |
Showing page {{ votes_pg.number }} of {{ votes_pg.paginator.num_pages }}
17 |
29 |
30 |
31 |
32 |
33 | #
34 | Voter ID
35 | Vote
36 | Timestamp
37 | Hash
38 | Block
39 |
40 |
41 |
42 | {% for ballot, h, bh in votes %}
43 |
44 | {{ forloop.counter }}
45 | {{ ballot.id }}
46 | {{ ballot.vote }}
47 | {{ ballot.timestamp|unix_to_date|date:"Y-m-d H:i:s" }}
48 | {{ h|truncatechars:20 }}
49 | go to block
50 |
51 | {% empty %}
52 | No record.
53 | {% endfor %}
54 |
55 |
56 |
57 | {% endblock %}
--------------------------------------------------------------------------------
/simulation/templatetags/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GottfriedCP/Blockchain-based-E-Voting-Simulation/148935558f5888779d3d4846779f3547c781fae0/simulation/templatetags/__init__.py
--------------------------------------------------------------------------------
/simulation/templatetags/sim_filters.py:
--------------------------------------------------------------------------------
1 | import datetime
2 | from django import template
3 |
4 | register = template.Library()
5 |
6 | @register.filter
7 | def unix_to_date(val):
8 | return datetime.datetime.fromtimestamp(val)
9 |
--------------------------------------------------------------------------------
/simulation/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 |
3 | # Create your tests here.
4 |
--------------------------------------------------------------------------------
/simulation/urls.py:
--------------------------------------------------------------------------------
1 | from django.urls import path
2 | from . import views
3 |
4 | app_name = 'simulation'
5 | urlpatterns = [
6 | path('generate/', views.generate, name='generate'),
7 | path('seal/', views.seal, name='seal'),
8 | path('transactions/', views.transactions, name='transactions'),
9 | path('blockchain/', views.blockchain, name='blockchain'),
10 |
11 | path('block//', views.block_detail, name='block_detail'),
12 |
13 | path('verify/', views.verify, name='verify'),
14 | path('sync/', views.sync, name='sync'),
15 | path('sync_block//', views.sync_block, name='sync_block'),
16 | ]
17 |
--------------------------------------------------------------------------------
/simulation/views.py:
--------------------------------------------------------------------------------
1 | import datetime, time, json, math
2 | from random import randint
3 | from uuid import uuid4
4 | from Crypto.Hash import SHA3_256
5 | from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator
6 | from django.conf import settings
7 | from django.contrib import messages
8 | from django.http import HttpResponse
9 | from django.shortcuts import get_object_or_404, render, redirect
10 |
11 | from .models import Vote, Block, VoteBackup
12 | from .merkle.merkle_tool import MerkleTools
13 |
14 | def generate(request):
15 | """Generate transactions and fill them with valid values."""
16 | number_of_transactions = settings.N_TRANSACTIONS
17 | number_of_tx_per_block = settings.N_TX_PER_BLOCK
18 |
19 | # Delete all data from previous demo.
20 | deleted_old_votes = Vote.objects.all().delete()[0]
21 | VoteBackup.objects.all().delete()
22 | print('\nDeleted {} data from previous simulation.\n'.format(deleted_old_votes))
23 | # Delete all blocks from previous demo.
24 | deleted_old_blocks = Block.objects.all().delete()[0]
25 | print("\nDeleted {} blocks from previous simulation.\n".format(deleted_old_blocks))
26 |
27 | # Generate transactions.
28 | time_start = time.time()
29 | block_no = 1
30 | for i in range(1, number_of_transactions + 1):
31 | # generate random, valid values
32 | v_id = str(uuid4())
33 | v_cand = _get_vote()
34 | v_timestamp = _get_timestamp()
35 | # directly fill the values and the block id for simulation purpose
36 | new_vote = Vote(id=v_id, vote=v_cand, timestamp=v_timestamp, block_id=block_no)
37 | new_backup_vote = VoteBackup(id=v_id, vote=v_cand, timestamp=v_timestamp, block_id=block_no)
38 | # "Broadcast" to two nodes
39 | new_vote.save()
40 | new_backup_vote.save()
41 | print("#{} new vote: {}".format(i, new_vote)) # for sanity
42 | if i % number_of_tx_per_block == 0:
43 | block_no += 1
44 | time_end = time.time()
45 | print('\nFinished in {} seconds.\n'.format(time_end - time_start))
46 |
47 | # View the generated transactions
48 | votes = Vote.objects.order_by('-timestamp')[:100] # only shows the last 100, if any
49 | context = {
50 | 'votes': votes,
51 | }
52 | request.session['transactions_done'] = True
53 | return render(request, 'simulation/generate.html', context)
54 |
55 | def seal(request):
56 | """Seal the transactions generated previously."""
57 | if request.session.get('transactions_done') is None:
58 | redirect('welcome:home')
59 | del request.session['transactions_done']
60 |
61 | # Puzzle requirement: '0' * (n leading zeros)
62 | puzzle, pcount = settings.PUZZLE, settings.PLENGTH
63 |
64 | # Seal transactions into blocks
65 | time_start = time.time()
66 | number_of_blocks = settings.N_BLOCKS
67 | prev_hash = '0' * 64
68 | for i in range(1, number_of_blocks + 1):
69 | block_transactions = Vote.objects.filter(block_id=i).order_by('timestamp')
70 | root = MerkleTools()
71 | root.add_leaf([str(tx) for tx in block_transactions], True)
72 | root.make_tree()
73 | merkle_h = root.get_merkle_root()
74 |
75 | # Try to seal the block and generate valid hash
76 | nonce = 0
77 | timestamp = datetime.datetime.now().timestamp()
78 | while True:
79 | enc = ("{}{}{}{}".format(prev_hash, merkle_h, nonce, timestamp)).encode('utf-8')
80 | h = SHA3_256.new(enc).hexdigest()
81 | if h[:pcount] == puzzle:
82 | break
83 | nonce += 1
84 |
85 | # Create the block
86 | block = Block(id=i, prev_h=prev_hash, merkle_h=merkle_h, h=h, nonce=nonce, timestamp=timestamp)
87 | block.save()
88 | print('\nBlock {} is mined\n'.format(i))
89 | # Set this hash as prev hash
90 | prev_hash = h
91 |
92 | time_end = time.time()
93 | print('\nSuccessfully created {} blocks.\n'.format(number_of_blocks))
94 | print('\nFinished in {} seconds.\n'.format(time_end - time_start))
95 | return redirect('simulation:blockchain')
96 |
97 | def transactions(request):
98 | """See all transactions that have been contained in blocks."""
99 | vote_list = Vote.objects.all().order_by('timestamp')
100 | paginator = Paginator(vote_list, 100, orphans=20, allow_empty_first_page=True)
101 |
102 | page = request.GET.get('page')
103 | votes = paginator.get_page(page)
104 |
105 | hashes = [SHA3_256.new(str(v).encode('utf-8')).hexdigest() for v in votes]
106 |
107 | # This happens if you don't use foreign key
108 | block_hashes = []
109 | for i in range(0, len(votes)):
110 | try:
111 | b = Block.objects.get(id=votes[i].block_id)
112 | h = b.h
113 | except:
114 | h = 404
115 | block_hashes.append(h)
116 |
117 | # zip the three iters
118 | votes_pg = votes # for pagination
119 | votes = zip(votes, hashes, block_hashes)
120 |
121 | # Calculate the voting result of 3 cands, the ugly way
122 | result = []
123 | for i in range(0, 3):
124 | try:
125 | r = Vote.objects.filter(vote=i+1).count()
126 | except:
127 | r = 0
128 | result.append(r)
129 |
130 | context = {
131 | 'votes': votes,
132 | 'result': result,
133 | 'votes_pg': votes_pg,
134 | }
135 | return render(request, 'simulation/transactions.html', context)
136 |
137 | def blockchain(request):
138 | """See all mined blocks."""
139 | blocks = Block.objects.all().order_by('id')
140 | context = {
141 | 'blocks': blocks,
142 | }
143 | return render(request, 'simulation/blockchain.html', context)
144 |
145 | def verify(request):
146 | """Verify transactions in all blocks by re-calculating the merkle root."""
147 | # Basically, by just creating a session (message) var
148 |
149 | print('verifying data...')
150 | number_of_blocks = Block.objects.all().count()
151 | corrupt_block_list = ''
152 | for i in range(1, number_of_blocks + 1):
153 | # Select block #i
154 | b = Block.objects.get(id=i)
155 |
156 | # Select all transactions in block #i
157 | transactions = Vote.objects.filter(block_id=i).order_by('timestamp')
158 |
159 | # Verify them
160 | root = MerkleTools()
161 | root.add_leaf([str(tx) for tx in transactions], True)
162 | root.make_tree()
163 | merkle_h = root.get_merkle_root()
164 |
165 | if b.merkle_h == merkle_h:
166 | message = 'Block {} verified.'.format(i)
167 | else:
168 | message = 'Block {} is TAMPERED'.format(i)
169 | corrupt_block_list += ' {}'.format(i)
170 | print('{}'.format(message))
171 | if len(corrupt_block_list) > 0:
172 | messages.warning(request, 'The following blocks have corrupted transactions: {}.'.format(corrupt_block_list), extra_tags='bg-danger')
173 | else:
174 | messages.info(request, 'All transactions in blocks are intact.', extra_tags='bg-info')
175 | return redirect('simulation:blockchain')
176 |
177 | def sync(request):
178 | """Restore transactions from honest node."""
179 | deleted_old_votes = Vote.objects.all().delete()[0]
180 | print('\nTrying to sync {} transactions with 1 node(s)...\n'.format(deleted_old_votes))
181 | bk_votes = VoteBackup.objects.all().order_by('timestamp')
182 | for bk_v in bk_votes:
183 | vote = Vote(id=bk_v.id, vote=bk_v.vote, timestamp=bk_v.timestamp, block_id=bk_v.block_id)
184 | vote.save()
185 | print('\nSync complete.\n')
186 | messages.info(request, 'All blocks have been synced successfully.')
187 | return redirect('simulation:blockchain')
188 |
189 | def sync_block(request, block_id):
190 | """Restore transactions of a block from honest node."""
191 | b = Block.objects.get(id=block_id)
192 | print('\nSyncing transactions in block {}\n'.format(b.id))
193 | # Get all existing transactions in this block and delete them
194 | Vote.objects.filter(block_id=block_id).delete()
195 | # Then rewrite from backup node
196 | bak_votes = VoteBackup.objects.filter(block_id=block_id).order_by('timestamp')
197 | for bv in bak_votes:
198 | v = Vote(id=bv.id, vote=bv.vote, timestamp=bv.timestamp, block_id=bv.block_id)
199 | v.save()
200 | # Just in case, delete transactions without valid block
201 | block_count = Block.objects.all().count()
202 | Vote.objects.filter(block_id__gt=block_count).delete()
203 | Vote.objects.filter(block_id__lt=1).delete()
204 | print('\nSync complete\n')
205 | return redirect('simulation:block_detail', block_hash=b.h)
206 |
207 | def block_detail(request, block_hash):
208 | """See the details of a block and its transactions."""
209 | # Select the block or 404
210 | block = get_object_or_404(Block, h=block_hash)
211 | confirmed_by = (Block.objects.all().count() - block.id) + 1
212 | # Select all corresponding transactions
213 | transaction_list = Vote.objects.filter(block_id=block.id).order_by('timestamp')
214 | paginator = Paginator(transaction_list, 100, orphans=20)
215 |
216 | page = request.GET.get('page')
217 | transactions = paginator.get_page(page)
218 | transactions_hashes = [SHA3_256.new(str(t).encode('utf-8')).hexdigest() for t in transactions]
219 |
220 | # Check the integrity of transactions
221 | root = MerkleTools()
222 | root.add_leaf([str(tx) for tx in transaction_list], True)
223 | root.make_tree()
224 | merkle_h = root.get_merkle_root()
225 | tampered = block.merkle_h != merkle_h
226 |
227 | transactions_pg = transactions # for pagination
228 | transactions = zip(transactions, transactions_hashes)
229 |
230 | # Get prev and next block id
231 | prev_block = Block.objects.filter(id=block.id - 1).first()
232 | next_block = Block.objects.filter(id=block.id + 1).first()
233 |
234 | context = {
235 | 'bk': block,
236 | 'confirmed_by': confirmed_by,
237 | 'transactions': transactions,
238 | 'tampered': tampered,
239 | 'verified_merkle_h': merkle_h,
240 | 'prev_block': prev_block,
241 | 'next_block': next_block,
242 | 'transactions_pg': transactions_pg,
243 | }
244 |
245 | return render(request, 'simulation/block.html', context)
246 |
247 | # HELPER FUNCTIONS
248 | def _get_vote():
249 | return randint(1, 3)
250 |
251 | def _get_timestamp():
252 | return datetime.datetime.now().timestamp()
253 |
--------------------------------------------------------------------------------
/static/css/main.css:
--------------------------------------------------------------------------------
1 | body {
2 | padding-top: 1%;
3 | padding-bottom: 1%;
4 | }
5 |
6 | form {
7 | padding: 1%;
8 | }
9 |
10 | img {
11 | max-width: 128px;
12 | height: auto;
13 | }
14 |
15 | table {
16 | display: block;
17 | overflow: auto;
18 | height: 320px;
19 | white-space: nowrap;
20 | }
21 |
22 | .menu {
23 | border: 1px solid black;
24 | min-height: 420px;
25 | margin: auto;
26 | position: relative;
27 | }
28 |
29 | .menu h2 {
30 | padding: 7% 0;
31 | text-align: center;
32 | }
33 |
34 | .menu ul, .menu p {
35 | margin: 0 20%;
36 | }
37 |
38 | div.btn-menu {
39 | position: absolute;
40 | bottom: 5%;
41 | left: 5%;
42 | width: 100%;
43 | }
44 |
45 | a.btn-menu {
46 | margin-top: 2%;
47 | width: 90%;
48 | }
49 |
50 | div#footer {
51 | padding-top: 5%;
52 | }
53 |
54 | div#footer #credit {
55 | text-align: center;
56 | }
--------------------------------------------------------------------------------
/static/js/main.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GottfriedCP/Blockchain-based-E-Voting-Simulation/148935558f5888779d3d4846779f3547c781fae0/static/js/main.js
--------------------------------------------------------------------------------
/templates/base.html:
--------------------------------------------------------------------------------
1 |
2 | {% load static %}
3 |
4 |
5 |
6 |
7 | Sim: {% block title_bl %}Blockchain-based e-voting demo{% endblock %}
8 |
9 |
10 |
11 |
12 |
13 |
14 | {% block body_bl %}
15 | {% endblock %}
16 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/welcome/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GottfriedCP/Blockchain-based-E-Voting-Simulation/148935558f5888779d3d4846779f3547c781fae0/welcome/__init__.py
--------------------------------------------------------------------------------
/welcome/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 |
3 | # Register your models here.
4 |
--------------------------------------------------------------------------------
/welcome/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class WelcomeConfig(AppConfig):
5 | name = 'welcome'
6 |
--------------------------------------------------------------------------------
/welcome/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GottfriedCP/Blockchain-based-E-Voting-Simulation/148935558f5888779d3d4846779f3547c781fae0/welcome/migrations/__init__.py
--------------------------------------------------------------------------------
/welcome/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 |
3 | # Create your models here.
4 |
--------------------------------------------------------------------------------
/welcome/templates/welcome/home.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 | {% load static %}
3 | {% block title_bl %}Home{% endblock %}
4 |
5 | {% block body_bl %}
6 |
7 |
8 |
Blockchain-based e-voting simulation
9 |
10 |
11 |
24 |
39 |
40 |
41 | {% endblock %}
--------------------------------------------------------------------------------
/welcome/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 |
3 | # Create your tests here.
4 |
--------------------------------------------------------------------------------
/welcome/urls.py:
--------------------------------------------------------------------------------
1 | from django.urls import path
2 | from . import views
3 |
4 | app_name = 'welcome'
5 | urlpatterns = [
6 | path('', views.home, name='home'),
7 | ]
8 |
--------------------------------------------------------------------------------
/welcome/views.py:
--------------------------------------------------------------------------------
1 | from django.conf import settings
2 | from django.http import HttpResponse
3 | from django.shortcuts import render
4 |
5 | def home(request):
6 | context = {
7 | 'tx': settings.N_TRANSACTIONS,
8 | 'bl': settings.N_BLOCKS,
9 | }
10 | return render(request, 'welcome/home.html', context)
11 |
--------------------------------------------------------------------------------