Esempio n. 1
0
    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)
Esempio n. 3
0
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")
Esempio n. 4
0
 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),
     )
Esempio n. 5
0
    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)