def verify_results(self) -> None: """Verify results of election""" # Deserialize description_from_file = ElectionDescription.from_json_file( DESCRIPTION_FILE_NAME, RESULTS_DIR ) self.assertEqual(self.description, description_from_file) context_from_file = CiphertextElectionContext.from_json_file( CONTEXT_FILE_NAME, RESULTS_DIR ) self.assertEqual(self.context, context_from_file) constants_from_file = ElectionConstants.from_json_file( CONSTANTS_FILE_NAME, RESULTS_DIR ) self.assertEqual(self.constants, constants_from_file) device_name = DEVICE_PREFIX + str(self.device.uuid) device_from_file = EncryptionDevice.from_json_file(device_name, DEVICES_DIR) self.assertEqual(self.device, device_from_file) ciphertext_ballots: List[CiphertextAcceptedBallot] = [] for ballot in self.ballot_store.all(): ballot_name = BALLOT_PREFIX + ballot.object_id ballot_from_file = CiphertextAcceptedBallot.from_json_file( ballot_name, BALLOTS_DIR ) self.assertEqual(ballot, ballot_from_file) spoiled_ballots: List[CiphertextAcceptedBallot] = [] for spoiled_ballot in self.ciphertext_tally.spoiled_ballots.values(): ballot_name = BALLOT_PREFIX + spoiled_ballot.object_id spoiled_ballot_from_file = CiphertextAcceptedBallot.from_json_file( ballot_name, SPOILED_DIR ) self.assertEqual(spoiled_ballot, spoiled_ballot_from_file) ciphertext_tally_from_file = PublishedCiphertextTally.from_json_file( ENCRYPTED_TALLY_FILE_NAME, RESULTS_DIR ) self.assertEqual( publish_ciphertext_tally(self.ciphertext_tally), ciphertext_tally_from_file ) plainttext_tally_from_file = PlaintextTally.from_json_file( TALLY_FILE_NAME, RESULTS_DIR ) self.assertEqual(self.plaintext_tally, plainttext_tally_from_file) coefficient_validation_sets: List[CoefficientValidationSet] = [] for coefficient_validation_set in self.coefficient_validation_sets: set_name = COEFFICIENT_PREFIX + coefficient_validation_set.owner_id coefficient_validation_set_from_file = CoefficientValidationSet.from_json_file( set_name, COEFFICIENTS_DIR ) self.assertEqual( coefficient_validation_set, coefficient_validation_set_from_file )
def test_publish(self) -> None: # Arrange now = datetime.now(timezone.utc) description = ElectionDescription("", ElectionType.unknown, now, now, [], [], [], [], [], []) context = make_ciphertext_election_context(1, 1, ONE_MOD_P, ONE_MOD_Q) constants = ElectionConstants() devices = [] coefficients = [CoefficientValidationSet("", [], [])] encrypted_ballots = [] spoiled_ballots = [] plaintext_tally = PlaintextTally("", [], []) ciphertext_tally = publish_ciphertext_tally( CiphertextTally("", description, context)) # Act publish( description, context, constants, devices, encrypted_ballots, spoiled_ballots, ciphertext_tally, plaintext_tally, coefficients, ) # Assert self.assertTrue(path.exists(RESULTS_DIR)) # Cleanup rmtree(RESULTS_DIR)
def _tally_ballots( tally: CiphertextTally, ballots: List[CiphertextAcceptedBallot], scheduler: Scheduler, ) -> Any: """ Append a series of ballots to a new or existing tally """ tally_succeeded = tally.batch_append(ballots, scheduler) if tally_succeeded: published_tally = publish_ciphertext_tally(tally) return published_tally.to_json_object() raise HTTPException(status_code=500, detail="Unable to tally ballots")
def publish_results(self) -> None: """ Publish results/artifacts of the election """ publish( self.description, self.context, self.constants, [self.device], self.ballot_store.all(), self.ciphertext_tally.spoiled_ballots.values(), publish_ciphertext_tally(self.ciphertext_tally), self.plaintext_tally, self.coefficient_validation_sets, ) self._assert_message( "Publish", f"Artifacts published to: {RESULTS_DIR}", path.exists(RESULTS_DIR), )
def generate( self, number_of_ballots: int = DEFAULT_NUMBER_OF_BALLOTS, cast_spoil_ratio: int = CAST_SPOIL_RATIO, ): """ Generate the sample data set """ # Clear the results directory rmtree(RESULTS_DIR, ignore_errors=True) # Configure the election ( public_data, private_data, ) = self.election_factory.get_hamilton_election_with_encryption_context( ) plaintext_ballots = self.ballot_factory.generate_fake_plaintext_ballots_for_election( public_data.metadata, number_of_ballots) self.encrypter = EncryptionMediator(public_data.metadata, public_data.context, self.encryption_device) # Encrypt some ballots ciphertext_ballots: List[CiphertextBallot] = [] for plaintext_ballot in plaintext_ballots: ciphertext_ballots.append( get_optional(self.encrypter.encrypt(plaintext_ballot))) ballot_store = BallotStore() ballot_box = BallotBox(public_data.metadata, public_data.context, ballot_store) # Randomly cast/spoil the ballots accepted_ballots: List[CiphertextAcceptedBallot] = [] for ballot in ciphertext_ballots: if randint(0, 100) % cast_spoil_ratio == 0: accepted_ballots.append(ballot_box.spoil(ballot)) else: accepted_ballots.append(ballot_box.cast(ballot)) # Tally ciphertext_tally = get_optional( tally_ballots(ballot_store, public_data.metadata, public_data.context)) # Decrypt decrypter = DecryptionMediator(public_data.metadata, public_data.context, ciphertext_tally) for i, guardian in enumerate(private_data.guardians): if i < QUORUM or not THRESHOLD_ONLY: decrypter.announce(guardian) plaintext_tally = get_optional(decrypter.get_plaintext_tally()) # Publish publish( public_data.description, public_data.context, public_data.constants, [self.encryption_device], accepted_ballots, ciphertext_tally.spoiled_ballots.values(), publish_ciphertext_tally(ciphertext_tally), plaintext_tally, public_data.guardians, ) publish_private_data(plaintext_ballots, ciphertext_ballots, private_data.guardians)