Dates, hashes and more cross-references
This commit is contained in:
parent
247f050d5b
commit
82aa31cf50
@ -32,10 +32,12 @@ class NullEncryptedAnswer(EncryptedAnswer):
|
|||||||
class Ballot(EmbeddedObject):
|
class Ballot(EmbeddedObject):
|
||||||
#_id = UUIDField()
|
#_id = UUIDField()
|
||||||
encrypted_answers = EmbeddedObjectListField()
|
encrypted_answers = EmbeddedObjectListField()
|
||||||
|
election_id = UUIDField()
|
||||||
|
election_hash = StringField()
|
||||||
|
|
||||||
class Vote(EmbeddedObject):
|
class Vote(EmbeddedObject):
|
||||||
ballot = EmbeddedObjectField()
|
ballot = EmbeddedObjectField()
|
||||||
cast_at = StringField()
|
cast_at = DateTimeField()
|
||||||
|
|
||||||
class Voter(EmbeddedObject):
|
class Voter(EmbeddedObject):
|
||||||
_id = UUIDField()
|
_id = UUIDField()
|
||||||
|
@ -81,6 +81,8 @@ class ElectionTestCase(EosTestCase):
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
election_hash = SHA256().update_obj(election).hash_as_b64()
|
||||||
|
|
||||||
# Open voting
|
# Open voting
|
||||||
self.do_task_assert(election, 'eos.base.workflow.TaskOpenVoting', 'eos.base.workflow.TaskCloseVoting')
|
self.do_task_assert(election, 'eos.base.workflow.TaskOpenVoting', 'eos.base.workflow.TaskCloseVoting')
|
||||||
election.save()
|
election.save()
|
||||||
@ -89,12 +91,12 @@ class ElectionTestCase(EosTestCase):
|
|||||||
VOTES = [[[0], [0]], [[0, 1], [1]], [[2], [0]]]
|
VOTES = [[[0], [0]], [[0, 1], [1]], [[2], [0]]]
|
||||||
|
|
||||||
for i in range(3):
|
for i in range(3):
|
||||||
ballot = Ballot()
|
ballot = Ballot(election_id=election._id, election_hash=election_hash)
|
||||||
for j in range(2):
|
for j in range(2):
|
||||||
answer = ApprovalAnswer(choices=VOTES[i][j])
|
answer = ApprovalAnswer(choices=VOTES[i][j])
|
||||||
encrypted_answer = NullEncryptedAnswer(answer=answer)
|
encrypted_answer = NullEncryptedAnswer(answer=answer)
|
||||||
ballot.encrypted_answers.append(encrypted_answer)
|
ballot.encrypted_answers.append(encrypted_answer)
|
||||||
vote = Vote(ballot=ballot)
|
vote = Vote(ballot=ballot, cast_at=DateTimeField.now())
|
||||||
election.voters[i].votes.append(vote)
|
election.voters[i].votes.append(vote)
|
||||||
|
|
||||||
election.save()
|
election.save()
|
||||||
|
@ -29,6 +29,7 @@ class WorkflowTask(EmbeddedObject):
|
|||||||
provides = []
|
provides = []
|
||||||
|
|
||||||
status = IntField(default=0, is_hashed=False)
|
status = IntField(default=0, is_hashed=False)
|
||||||
|
exited_at = DateTimeField(is_hashed=False)
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
@ -81,7 +82,7 @@ class WorkflowTask(EmbeddedObject):
|
|||||||
listener()
|
listener()
|
||||||
|
|
||||||
def on_exit(self):
|
def on_exit(self):
|
||||||
pass
|
self.exited_at = DateTimeField.now()
|
||||||
|
|
||||||
def exit(self):
|
def exit(self):
|
||||||
if self.status is not WorkflowTask.Status.ENTERED:
|
if self.status is not WorkflowTask.Status.ENTERED:
|
||||||
|
@ -76,7 +76,6 @@ class BigInt(EosObject):
|
|||||||
setattr(self, key, make_operator_func(func))
|
setattr(self, key, make_operator_func(func))
|
||||||
|
|
||||||
for key, func in [
|
for key, func in [
|
||||||
('__eq__', lambda x: x == 0),
|
|
||||||
('__ne__', lambda x: x != 0),
|
('__ne__', lambda x: x != 0),
|
||||||
('__lt__', lambda x: x < 0),
|
('__lt__', lambda x: x < 0),
|
||||||
('__gt__', lambda x: x > 0),
|
('__gt__', lambda x: x > 0),
|
||||||
@ -104,6 +103,12 @@ class BigInt(EosObject):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return str(self.impl)
|
return str(self.impl)
|
||||||
|
|
||||||
|
# TNYI: Transcrypt doesn't like that we've defined __eq__ in EosObject
|
||||||
|
def __eq__(self, other):
|
||||||
|
if not isinstance(other, BigInt):
|
||||||
|
other = BigInt(other)
|
||||||
|
return self.impl.compareTo(other.impl) == 0
|
||||||
|
|
||||||
def __int__(self):
|
def __int__(self):
|
||||||
# WARNING: This will yield unexpected results for large numbers
|
# WARNING: This will yield unexpected results for large numbers
|
||||||
return int(str(self.impl))
|
return int(str(self.impl))
|
||||||
|
@ -31,6 +31,7 @@ if is_python:
|
|||||||
from bson.binary import UUIDLegacy
|
from bson.binary import UUIDLegacy
|
||||||
|
|
||||||
import base64
|
import base64
|
||||||
|
from datetime import datetime
|
||||||
import hashlib
|
import hashlib
|
||||||
import json
|
import json
|
||||||
import uuid
|
import uuid
|
||||||
@ -119,6 +120,37 @@ if is_python:
|
|||||||
else:
|
else:
|
||||||
UUIDField = PrimitiveField
|
UUIDField = PrimitiveField
|
||||||
|
|
||||||
|
class DateTimeField(Field):
|
||||||
|
def pad(self, number):
|
||||||
|
if number < 10:
|
||||||
|
return '0' + str(number)
|
||||||
|
return str(number)
|
||||||
|
|
||||||
|
def serialise(self, value, for_hash=False, should_protect=False):
|
||||||
|
if value is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
if is_python:
|
||||||
|
return value.strftime('%Y-%m-%dT%H:%M:%SZ')
|
||||||
|
else:
|
||||||
|
return value.getUTCFullYear() + '-' + self.pad(value.getUTCMonth() + 1) + '-' + self.pad(value.getUTCDate()) + 'T' + self.pad(value.getUTCHours()) + ':' + self.pad(value.getUTCMinutes()) + ':' + self.pad(value.getUTCSeconds()) + 'Z'
|
||||||
|
|
||||||
|
def deserialise(self, value):
|
||||||
|
if value is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
if is_python:
|
||||||
|
return datetime.strptime(value, '%Y-%m-%dT%H:%M:%SZ')
|
||||||
|
else:
|
||||||
|
return Date.parse(value)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def now():
|
||||||
|
if is_python:
|
||||||
|
return datetime.utcnow()
|
||||||
|
else:
|
||||||
|
return __pragma__('js', '{}', 'new Date()')
|
||||||
|
|
||||||
# Objects
|
# Objects
|
||||||
# =======
|
# =======
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
from eos.core.tests import *
|
from eos.core.tests import *
|
||||||
|
|
||||||
from eos.core.objects import __pragma__
|
from eos.core.objects import *
|
||||||
from eos.core.bigint import *
|
from eos.core.bigint import *
|
||||||
from eos.core.hashing import *
|
from eos.core.hashing import *
|
||||||
from eos.psr.bitstream import *
|
from eos.psr.bitstream import *
|
||||||
@ -26,6 +26,8 @@ from eos.psr.mixnet import *
|
|||||||
from eos.psr.secretsharing import *
|
from eos.psr.secretsharing import *
|
||||||
from eos.psr.workflow import *
|
from eos.psr.workflow import *
|
||||||
|
|
||||||
|
from eos.core.objects import __pragma__
|
||||||
|
|
||||||
class GroupValidityTestCase(EosTestCase):
|
class GroupValidityTestCase(EosTestCase):
|
||||||
# HAC 4.24
|
# HAC 4.24
|
||||||
def miller_rabin_test(self, n, t):
|
def miller_rabin_test(self, n, t):
|
||||||
@ -252,6 +254,8 @@ class ElectionTestCase(EosTestCase):
|
|||||||
# Freeze election
|
# Freeze election
|
||||||
self.do_task_assert(election, 'eos.base.workflow.TaskConfigureElection', 'eos.base.workflow.TaskOpenVoting')
|
self.do_task_assert(election, 'eos.base.workflow.TaskConfigureElection', 'eos.base.workflow.TaskOpenVoting')
|
||||||
|
|
||||||
|
election_hash = SHA256().update_obj(election).hash_as_b64() # Keep track of the hash and make sure it doesn't change
|
||||||
|
|
||||||
# Open voting
|
# Open voting
|
||||||
self.do_task_assert(election, 'eos.base.workflow.TaskOpenVoting', 'eos.base.workflow.TaskCloseVoting')
|
self.do_task_assert(election, 'eos.base.workflow.TaskOpenVoting', 'eos.base.workflow.TaskCloseVoting')
|
||||||
election.save()
|
election.save()
|
||||||
@ -260,12 +264,12 @@ class ElectionTestCase(EosTestCase):
|
|||||||
VOTES = [[[0], [0]], [[0, 1], [1]], [[2], [0]]]
|
VOTES = [[[0], [0]], [[0, 1], [1]], [[2], [0]]]
|
||||||
|
|
||||||
for i in range(3):
|
for i in range(3):
|
||||||
ballot = Ballot()
|
ballot = Ballot(election_id=election._id, election_hash=election_hash)
|
||||||
for j in range(2):
|
for j in range(2):
|
||||||
answer = ApprovalAnswer(choices=VOTES[i][j])
|
answer = ApprovalAnswer(choices=VOTES[i][j])
|
||||||
encrypted_answer = BlockEncryptedAnswer.encrypt(election.sk.public_key, answer)
|
encrypted_answer = BlockEncryptedAnswer.encrypt(election.sk.public_key, answer)
|
||||||
ballot.encrypted_answers.append(encrypted_answer)
|
ballot.encrypted_answers.append(encrypted_answer)
|
||||||
vote = Vote(ballot=ballot)
|
vote = Vote(ballot=ballot, cast_at=DateTimeField.now())
|
||||||
election.voters[i].votes.append(vote)
|
election.voters[i].votes.append(vote)
|
||||||
|
|
||||||
election.save()
|
election.save()
|
||||||
@ -301,6 +305,9 @@ class ElectionTestCase(EosTestCase):
|
|||||||
# Release result
|
# Release result
|
||||||
self.do_task_assert(election, 'eos.base.workflow.TaskReleaseResults', None)
|
self.do_task_assert(election, 'eos.base.workflow.TaskReleaseResults', None)
|
||||||
election.save()
|
election.save()
|
||||||
|
|
||||||
|
# Check the hash hasn't changed during that
|
||||||
|
self.assertEqual(SHA256().update_obj(election).hash_as_b64(), election_hash)
|
||||||
|
|
||||||
class PVSSTestCase(EosTestCase):
|
class PVSSTestCase(EosTestCase):
|
||||||
@py_only
|
@py_only
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
import click
|
import click
|
||||||
import flask
|
import flask
|
||||||
|
|
||||||
|
from eos.core.objects import *
|
||||||
from eos.base.election import *
|
from eos.base.election import *
|
||||||
from eos.psr.crypto import *
|
from eos.psr.crypto import *
|
||||||
from eos.psr.election import *
|
from eos.psr.election import *
|
||||||
@ -170,7 +171,7 @@ def election_api_cast_vote(election):
|
|||||||
|
|
||||||
# Cast the vote
|
# Cast the vote
|
||||||
ballot = EosObject.deserialise_and_unwrap(data['ballot'])
|
ballot = EosObject.deserialise_and_unwrap(data['ballot'])
|
||||||
vote = Vote(ballot=ballot, cast_at=datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ'))
|
vote = Vote(ballot=ballot, cast_at=DateTimeField.now())
|
||||||
voter.votes.append(vote)
|
voter.votes.append(vote)
|
||||||
|
|
||||||
election.save()
|
election.save()
|
||||||
|
@ -37,13 +37,13 @@
|
|||||||
<p>
|
<p>
|
||||||
Voting in this election
|
Voting in this election
|
||||||
{% if election.workflow.get_task('eos.base.workflow.TaskOpenVoting').status == Status.EXITED %}
|
{% if election.workflow.get_task('eos.base.workflow.TaskOpenVoting').status == Status.EXITED %}
|
||||||
opened
|
opened at {{ election.workflow.get_task('eos.base.workflow.TaskOpenVoting').exited_at.strftime('%Y-%m-%d %H:%M:%S') }} UTC
|
||||||
{% else %}
|
{% else %}
|
||||||
is scheduled to open
|
is scheduled to open
|
||||||
{% endif %}
|
{% endif %}
|
||||||
at the administrators' discretion, and
|
at the administrators' discretion, and
|
||||||
{% if election.workflow.get_task('eos.base.workflow.TaskCloseVoting').status == Status.EXITED %}
|
{% if election.workflow.get_task('eos.base.workflow.TaskCloseVoting').status == Status.EXITED %}
|
||||||
closed
|
closed at {{ election.workflow.get_task('eos.base.workflow.TaskCloseVoting').exited_at.strftime('%Y-%m-%d %H:%M:%S') }} UTC
|
||||||
{% else %}
|
{% else %}
|
||||||
is scheduled to close
|
is scheduled to close
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -58,6 +58,8 @@
|
|||||||
{% if election.workflow.get_task('eos.base.workflow.TaskReleaseResults').status == Status.EXITED %}
|
{% if election.workflow.get_task('eos.base.workflow.TaskReleaseResults').status == Status.EXITED %}
|
||||||
<h2>Results</h2>
|
<h2>Results</h2>
|
||||||
|
|
||||||
|
<p>Results were released at {{ election.workflow.get_task('eos.base.workflow.TaskReleaseResults').exited_at.strftime('%Y-%m-%d %H:%M:%S') }} UTC.</p>
|
||||||
|
|
||||||
{% for question in election.questions %}
|
{% for question in election.questions %}
|
||||||
<h3>{{ loop.index }}. {{ question.prompt }}</h2>
|
<h3>{{ loop.index }}. {{ question.prompt }}</h2>
|
||||||
{% include eosweb.core.main.model_view_map[question.__class__]['result_raw'] %}
|
{% include eosweb.core.main.model_view_map[question.__class__]['result_raw'] %}
|
||||||
|
Loading…
Reference in New Issue
Block a user