├── .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 | ![Ballot page](https://raw.githubusercontent.com/GottfriedCP/Blockchain-based-E-Voting-Simulation/master/screenshots/ballot.PNG) 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 | ![Sealed ballot](https://raw.githubusercontent.com/GottfriedCP/Blockchain-based-E-Voting-Simulation/master/screenshots/transactions.PNG) 22 | 23 | ![Block](https://raw.githubusercontent.com/GottfriedCP/Blockchain-based-E-Voting-Simulation/master/screenshots/block.PNG) 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 |
8 | {% csrf_token %} 9 |
10 | 11 | 12 |
13 |
14 |

Eligible candidates are shown below:

15 |
16 |
17 |

18 |

Freddie Mercury (1)

19 |
20 |
21 |

22 |

Jon Bon Jovi (2)

23 |
24 |
25 |

26 |

Suicidal Guy from Colourbox (3)

27 |
28 |
29 |
30 |
31 | 32 | 33 |
34 |
35 | 36 | 37 | 38 | The matching public key must already be registered. 39 | 40 |
41 | 42 |
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 | 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 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | {% for t, h in transactions %} 79 | 80 | 82 | 83 | 84 | 85 | 86 | {% endfor %} 87 | 88 |
#Voter IDCandTimestampHash
{{ forloop.counter }} 81 | {{ t.id }}{{ t.vote }}{{ t.timestamp|unix_to_date|date:"Y-m-d H:i:s" }}{{ h|truncatechars:32 }}
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 |
13 |

{{ message }}

14 |
15 | {% endfor %} 16 | {% endif %} 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | {% for block in blocks %} 30 | 31 | 33 | 34 | 35 | 36 | 37 | 38 | {% empty %} 39 |

No record.

40 | {% endfor %} 41 | 42 |
#Previous HashBlock HashMerkle Root HashNonceTimestamp
{{ block.id }} 32 | {{ block.prev_h|truncatechars:30 }}{{ block.h|truncatechars:30 }}{{ block.merkle_h|truncatechars:30 }}{{ block.nonce }}{{ block.timestamp|unix_to_date|date:"Y-m-d H:i:s" }}
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 | 13 | 14 | 15 | 16 | 17 | 18 | {% for ballot in votes %} 19 | 20 | 22 | 23 | 24 | {% endfor %} 25 | 26 |
Voter IDVoteTimestamp
{{ ballot.id }} 21 | {{ ballot.vote }}{{ ballot.timestamp }}
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 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | {% for ballot, h, bh in votes %} 43 | 44 | 45 | 47 | 48 | 49 | 50 | 51 | {% empty %} 52 |

No record.

53 | {% endfor %} 54 | 55 |
#Voter IDVoteTimestampHashBlock
{{ forloop.counter }}{{ ballot.id }} 46 | {{ ballot.vote }}{{ ballot.timestamp|unix_to_date|date:"Y-m-d H:i:s" }}{{ h|truncatechars:20 }}go to block
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 | --------------------------------------------------------------------------------