Dates, hashes and more cross-references

This commit is contained in:
RunasSudo 2017-11-24 20:26:18 +11:00
parent 247f050d5b
commit 82aa31cf50
Signed by: RunasSudo
GPG Key ID: 7234E476BF21C61A
8 changed files with 63 additions and 11 deletions

View File

@ -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()

View File

@ -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()

View File

@ -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:

View File

@ -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))

View File

@ -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
# ======= # =======

View File

@ -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

View File

@ -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()

View File

@ -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'] %}