com.vvote.verifier.component.votePacking.VotePackingDataStore.java Source code

Java tutorial

Introduction

Here is the source code for com.vvote.verifier.component.votePacking.VotePackingDataStore.java

Source

/**
 * This file is part of vVoteVerifier which is designed to be used as a verifiation tool for the vVote Election System.
 * Copyright (C) 2014  James Rumble (jerumble@gmail.com)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.vvote.verifier.component.votePacking;

import java.io.File;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import org.apache.commons.io.FilenameUtils;
import org.bouncycastle.math.ec.ECPoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.vvote.commits.CommitIdentifier;
import com.vvote.commits.FinalCommitment;
import com.vvote.commits.exceptions.CommitIdentifierException;
import com.vvote.datafiles.RaceMapFile;
import com.vvote.datafiles.commits.votes.VotingProcess;
import com.vvote.datafiles.exceptions.MixDataException;
import com.vvote.datafiles.exceptions.RaceMapException;
import com.vvote.datafiles.exceptions.VoteMessageCommitException;
import com.vvote.datafiles.mix.MixOutput;
import com.vvote.datafiles.mix.RaceIdentifier;
import com.vvote.ec.ElGamalECPoint;
import com.vvote.messages.typed.TypedJSONMessage;
import com.vvote.messages.typed.vote.CancelMessage;
import com.vvote.messages.typed.vote.PODMessage;
import com.vvote.messages.typed.vote.VoteMessage;
import com.vvote.messages.types.MessageType;
import com.vvote.thirdparty.json.orgjson.JSONArray;
import com.vvote.thirdparty.json.orgjson.JSONException;
import com.vvote.thirdparty.json.orgjson.JSONObject;
import com.vvote.verifier.component.ComponentDataStore;
import com.vvote.verifier.exceptions.ComponentDataStoreException;
import com.vvote.verifier.exceptions.ComponentSpecException;
import com.vvote.verifier.exceptions.ConfigException;
import com.vvote.verifier.exceptions.DataStoreException;
import com.vvote.verifier.exceptions.SpecException;
import com.vvote.verifierlibrary.exceptions.ASN1Exception;
import com.vvote.verifierlibrary.exceptions.JSONIOException;
import com.vvote.verifierlibrary.utils.Utils;
import com.vvote.verifierlibrary.utils.comparators.BallotSerialNumberComparator;
import com.vvote.verifierlibrary.utils.crypto.ECUtils;
import com.vvote.verifierlibrary.utils.io.ASN1ToJSONConverter;
import com.vvote.verifierlibrary.utils.io.FileType;
import com.vvote.verifierlibrary.utils.io.IOUtils;

/**
 * Specific data store object for the vote packing data
 * 
 * @author James Rumble
 * 
 */
public class VotePackingDataStore extends ComponentDataStore {

    /**
     * provides logging for the class
     */
    private static final Logger logger = LoggerFactory.getLogger(VotePackingDataStore.class);

    /**
     * Provides logging for the actual results produced in the verifier
     */
    private static final Logger resultsLogger = LoggerFactory.getLogger("results");

    /**
     * vote packing config
     */
    private VotePackingConfig votePackingConfig;

    /**
     * Holds the path to the mix input data
     */
    private String mixInputPath;

    /**
     * Holds the path to the mix output data
     */
    private String mixOutputPath;

    /**
     * Holds a list of cancelled ballots
     */
    private Map<CommitIdentifier, Map<String, CancelMessage>> cancelMessages;

    /**
     * Holds a list of vote messages
     */
    private Map<CommitIdentifier, Map<String, VoteMessage>> voteMessages;

    /**
     * Holds a list of pod messages
     */
    private Map<CommitIdentifier, Map<String, PODMessage>> podMessages;

    /**
     * Holds a list of related pod and vote messages
     */
    private Map<String, VotingProcess> votingProcesses;

    /**
     * mixnet input data
     */
    private Map<RaceIdentifier, List<List<ElGamalECPoint>>> mixInput = null;

    /**
     * mixnet output data
     */
    private Map<RaceIdentifier, List<List<ECPoint>>> mixOutput = null;

    /**
     * mixnet output preferences
     */
    private Map<RaceIdentifier, MixOutput> mixOutputPreferences = null;

    /**
     * The unencrypted padding point
     */
    private ECPoint paddingPoint = null;

    /**
     * The encrypted padding point
     */
    private ElGamalECPoint encryptedPaddingPoint = null;

    /**
     * The race map
     */
    private RaceMapFile raceMap = null;

    /**
     * Flag for whether the race map has been provided
     */
    private boolean hasRaceMap = false;

    /**
     * Constructor for a ballot gen data store object
     * 
     * @param spec
     * @param basePath
     * @param useExtraCommits
     * @throws ComponentDataStoreException
     * @throws DataStoreException
     */
    public VotePackingDataStore(VotePackingVerifierSpec spec, String basePath, boolean useExtraCommits)
            throws ComponentDataStoreException, DataStoreException {
        super(spec, basePath, useExtraCommits);

        logger.debug("Constructing VotePacking Data Store");

        this.voteMessages = new HashMap<CommitIdentifier, Map<String, VoteMessage>>();
        this.cancelMessages = new HashMap<CommitIdentifier, Map<String, CancelMessage>>();
        this.podMessages = new HashMap<CommitIdentifier, Map<String, PODMessage>>();
        this.votingProcesses = new TreeMap<String, VotingProcess>(new BallotSerialNumberComparator());
    }

    @Override
    public boolean readData() {

        logger.info("Reading in Election data to verify the Vote Packing process");

        boolean result = true;

        if (!this.hasReadData()) {
            result = super.readData();

            if (result) {
                try {
                    this.mixInputPath = IOUtils.findFile(this.getSpec().getMixInputFolder(), this.getBasePath());
                    this.mixOutputPath = IOUtils.findFile(this.getSpec().getMixOutputFolder(), this.getBasePath());

                    try {
                        this.votePackingConfig = new VotePackingConfig(
                                IOUtils.findFile(getSpec().getVotePackingConfig(), this.getBasePath()));
                    } catch (ConfigException e) {
                        logger.error("Unable to create a read data.", e);
                        return false;
                    }

                    String raceMapPath = IOUtils.findFile(this.getSpec().getRaceMap(), this.getBasePath());

                    if (raceMapPath != null) {
                        this.raceMap = new RaceMapFile(raceMapPath);
                        this.hasRaceMap = true;
                    }

                    this.loadMixOutputPreferences();

                    this.loadPaddingPoints();

                    this.loadMixInputData();

                    this.loadMixOutputData();

                } catch (MixDataException e) {
                    logger.error("Unable to create a read data.", e);
                    return false;
                } catch (JSONException e) {
                    logger.error("Unable to create a read data.", e);
                    return false;
                } catch (JSONIOException e) {
                    logger.error("Unable to create a read data.", e);
                    return false;
                } catch (ASN1Exception e) {
                    logger.error("Unable to create a read data.", e);
                    return false;
                } catch (RaceMapException e) {
                    logger.error("Unable to create a read data.", e);
                    return false;
                }

                if (!this.organiseVoteMessages()) {
                    logger.error("Unable to create a read data.");
                    return false;
                }

            } else {
                return false;
            }
        }

        return true;
    }

    /**
     * Gets the race map file
     * 
     * @return raceMap
     */
    public RaceMapFile getRaceMap() {
        return this.raceMap;
    }

    /**
     * Load the mixnet output data
     * 
     * @throws JSONException
     * @throws MixDataException
     * @throws ASN1Exception
     * @throws JSONIOException
     */
    private void loadMixOutputData() throws JSONException, MixDataException, ASN1Exception, JSONIOException {
        this.mixOutput = new HashMap<RaceIdentifier, List<List<ECPoint>>>();

        final File mixOutputDirectory = new File(this.mixOutputPath);

        logger.debug("Loading in the mix output data from the folder: {}", mixOutputDirectory);

        // check whether the provided directory is valid
        if (!mixOutputDirectory.isDirectory()) {
            logger.error("The mix output folder must be a directory: {}", mixOutputDirectory);
            throw new MixDataException("The mix output folder must be a directory: " + mixOutputDirectory);
        }

        String jsonFile = null;

        RaceIdentifier currentIdentifier = null;

        JSONArray mixOutputArray = null;

        JSONArray plaintexts = null;

        List<ECPoint> currentIds = null;

        List<List<ECPoint>> currentFileIds = null;

        // loop over each file in the directory
        for (File file : mixOutputDirectory.listFiles()) {

            logger.debug("Current file in mix output directory: {}", file.getName());

            if (IOUtils.checkExtension(FileType.MIX_OUTPUT, file.getName())) {

                // get equivalent json filename
                jsonFile = IOUtils.addExtension(FilenameUtils.removeExtension(file.getPath()), FileType.JSON);

                // convert from asn.1 to json format
                ASN1ToJSONConverter.asn1ToJSON(file.getPath(), jsonFile, FileType.MIX_OUTPUT);

                // get current identifier
                currentIdentifier = Utils.getRaceIdentifierFromFileName(jsonFile, this.hasRaceMap);

                mixOutputArray = IOUtils.readJSONArrayFromFile(jsonFile);

                currentFileIds = new ArrayList<List<ECPoint>>();

                // loop over each ballot
                for (int i = 0; i < mixOutputArray.length(); i++) {

                    currentIds = new ArrayList<ECPoint>();

                    plaintexts = mixOutputArray.getJSONArray(i);

                    // loop over each plaintext
                    for (int j = 0; j < plaintexts.length(); j++) {
                        currentIds.add(ECUtils.constructECPointFromJSON(plaintexts.getJSONObject(j)));
                    }

                    currentFileIds.add(currentIds);
                }

                this.mixOutput.put(currentIdentifier, currentFileIds);
            }
        }

        logger.debug("Successfully loaded mixnet output data");
    }

    /**
     * Loads the mix input data
     * 
     * @throws ASN1Exception
     * @throws MixDataException
     * @throws JSONIOException
     * @throws JSONException
     */
    private void loadMixInputData() throws ASN1Exception, MixDataException, JSONIOException, JSONException {

        this.mixInput = new HashMap<RaceIdentifier, List<List<ElGamalECPoint>>>();

        final File mixInputDirectory = new File(this.mixInputPath);

        logger.debug("Loading in the mix input data from the folder: {}", mixInputDirectory);

        // check whether the provided directory is valid
        if (!mixInputDirectory.isDirectory()) {
            logger.error("The mix input folder must be a directory: {}", mixInputDirectory);
            throw new MixDataException("The mix input folder must be a directory: " + mixInputDirectory);
        }

        String jsonFile = null;

        RaceIdentifier currentIdentifier = null;

        JSONArray mixInputArray = null;

        JSONArray ciphers = null;

        List<ElGamalECPoint> currentPackings = null;

        List<List<ElGamalECPoint>> currentFilePackings = null;

        // loop over each file in the directory
        for (File file : mixInputDirectory.listFiles()) {

            logger.debug("Current file in mix input directory: {}", file.getName());

            if (IOUtils.checkExtension(FileType.MIX_INPUT, file.getName())) {

                // get equivalent json filename
                jsonFile = IOUtils.addExtension(FilenameUtils.removeExtension(file.getPath()), FileType.JSON);

                // convert from asn.1 to json format
                ASN1ToJSONConverter.asn1ToJSON(file.getPath(), jsonFile, FileType.MIX_INPUT);

                // get current identifier
                currentIdentifier = Utils.getRaceIdentifierFromFileName(jsonFile, this.hasRaceMap);

                mixInputArray = IOUtils.readJSONArrayFromFile(jsonFile);

                currentFilePackings = new ArrayList<List<ElGamalECPoint>>();

                // loop over each ballot
                for (int i = 0; i < mixInputArray.length(); i++) {

                    currentPackings = new ArrayList<ElGamalECPoint>();

                    ciphers = mixInputArray.getJSONArray(i);

                    // loop over each cipher
                    for (int j = 0; j < ciphers.length(); j++) {
                        currentPackings.add(ECUtils.constructElGamalECPointFromJSON(ciphers.getJSONObject(j)));
                    }

                    currentFilePackings.add(currentPackings);
                }

                this.mixInput.put(currentIdentifier, currentFilePackings);
            }
        }

        logger.debug("Successfully loaded mixnet input data");
    }

    /**
     * Helper method to load the mixnet output preferences from csv files
     * 
     * @throws MixDataException
     */
    private void loadMixOutputPreferences() throws MixDataException {
        this.mixOutputPreferences = new HashMap<RaceIdentifier, MixOutput>();

        final File mixOutputDirectory = new File(this.mixOutputPath);

        logger.debug("Loading in the mix output preferences from the folder: {}", mixOutputDirectory);

        // check whether the provided directory is valid
        if (!mixOutputDirectory.isDirectory()) {
            logger.error("The mix output folder must be a directory: {}", mixOutputDirectory);
            throw new MixDataException("The mix output folder must be a directory: " + mixOutputDirectory);
        }

        // loop over each file in the directory
        for (File file : mixOutputDirectory.listFiles()) {

            logger.debug("Current file in mix output directory: {}", file.getName());

            if (IOUtils.checkExtension(FileType.CSV, file.getName())) {
                this.addMixOutput(file.toString());
            }
        }

        logger.debug("Successfully loaded mixnet output preferences");
    }

    /**
     * Loads the padding points to be used after vote packing has taken place to
     * ensure all packed votes have the same number of columns
     * 
     * @return true if the padding points are loaded correctly
     * @throws JSONException
     * @throws JSONIOException
     */
    private boolean loadPaddingPoints() throws JSONException, JSONIOException {
        String paddingPointFile = IOUtils.findFile(this.getVotePackingConfig().getPaddingFile(),
                this.getBasePath());

        // read padding point for padding the reordered packed plaintext
        // candidate ids
        this.paddingPoint = ECUtils.constructECPointFromJSON(IOUtils.readJSONObjectFromFile(paddingPointFile));

        this.encryptedPaddingPoint = ECUtils.encrypt(this.paddingPoint, this.getPublicKey(), BigInteger.ONE);

        return true;
    }

    /**
     * Helper method for creating and adding a <code>MixOutput</code> file
     * containing preferences
     * 
     * @param mixOutputFile
     * @throws MixDataException
     */
    private void addMixOutput(String mixOutputFile) throws MixDataException {

        // read the input csv and construct a MixOutput object
        MixOutput currentMixOutput = new MixOutput(mixOutputFile, this.hasRaceMap);
        this.mixOutputPreferences.put(currentMixOutput.getIdentifier(), currentMixOutput);
    }

    /**
     * Helper method used to organise voting messages together grouping them by
     * serial numbers
     * 
     * @return true if the organisation took place successfully
     */
    private boolean organiseVoteMessages() {

        logger.debug("Organising Voting messages together");

        Map<String, PODMessage> podMessageMap = null;
        Map<String, VoteMessage> voteMessageMap = null;

        try {
            for (CommitIdentifier podIdentifier : this.podMessages.keySet()) {
                podMessageMap = this.podMessages.get(podIdentifier);

                for (String serialNumber : podMessageMap.keySet()) {
                    for (CommitIdentifier voteIdentifier : this.voteMessages.keySet()) {
                        voteMessageMap = this.voteMessages.get(voteIdentifier);

                        if (voteMessageMap.containsKey(serialNumber)) {
                            this.votingProcesses.put(serialNumber, new VotingProcess(
                                    podMessageMap.get(serialNumber), voteMessageMap.get(serialNumber)));
                        }
                    }
                }
            }
        } catch (VoteMessageCommitException e) {
            logger.error("Unable to organise voting messages: {}", e);
            return false;
        }

        logger.debug("Removing cancelled ballots");

        Map<String, CancelMessage> cancelMessageMap = null;

        for (CommitIdentifier cancelIdentifier : this.cancelMessages.keySet()) {

            logger.debug("Removing cancelled ballot: {}", cancelIdentifier);

            cancelMessageMap = this.cancelMessages.get(cancelIdentifier);

            for (String serialNumber : cancelMessageMap.keySet()) {

                if (this.votingProcesses.containsKey(serialNumber)) {
                    this.votingProcesses.remove(serialNumber);
                    logger.debug("Removing cancelled ballot: {}", serialNumber);
                }
            }
        }

        logger.debug("Successfully organised Voting messages together");

        return true;
    }

    /**
     * Constructor for a ballot gen data store object
     * 
     * @param spec
     * @param basePath
     * @param useExtraCommits
     * @throws ComponentDataStoreException
     * @throws ComponentSpecException
     * @throws DataStoreException
     * @throws SpecException
     */
    public VotePackingDataStore(JSONObject spec, String basePath, boolean useExtraCommits)
            throws ComponentDataStoreException, ComponentSpecException, DataStoreException, SpecException {
        this(new VotePackingVerifierSpec(spec), basePath, useExtraCommits);
    }

    @Override
    public List<MessageType> initialiseListOfRelevantMessages() {

        List<MessageType> relevantTypes = super.initialiseListOfRelevantMessages();
        relevantTypes.add(MessageType.VOTE);
        relevantTypes.add(MessageType.POD);
        relevantTypes.add(MessageType.CANCEL);
        relevantTypes.add(MessageType.AUDIT);
        return relevantTypes;
    }

    @Override
    public VotePackingVerifierSpec getSpec() {
        if (super.getSpec() instanceof VotePackingVerifierSpec) {
            return (VotePackingVerifierSpec) super.getSpec();
        }
        return null;
    }

    /**
     * Getter for the votePackingConfig
     * 
     * @return votePackingConfig
     */
    public VotePackingConfig getVotePackingConfig() {
        return this.votePackingConfig;
    }

    /**
     * Getter for the mix input data path
     * 
     * @return mixInputPath
     */
    public String getMixInputPath() {
        return this.mixInputPath;
    }

    /**
     * Getter for the mix output data path
     * 
     * @return mixOutputPath
     */
    public String getMixOutputPath() {
        return this.mixOutputPath;
    }

    /**
     * Adds a <code>TypedJSONMessage</code> from the
     * <code>FinalCommitment</code> object
     * 
     * @param typedMessage
     * @param commitment
     */
    @Override
    public boolean addMessage(TypedJSONMessage typedMessage, FinalCommitment commitment) {
        if (!super.addMessage(typedMessage, commitment)) {
            return false;
        }
        try {
            if (typedMessage.getType().equals(MessageType.VOTE)) {
                this.addVoteMessage(typedMessage, commitment);
            } else if (typedMessage.getType().equals(MessageType.POD)) {
                this.addPODMessage(typedMessage, commitment);
            } else if (typedMessage.getType().equals(MessageType.CANCEL)) {
                this.addCancelMessage(typedMessage, commitment);
            }
        } catch (CommitIdentifierException e) {
            logger.error("Unable to add message: {}", typedMessage, e);
            return false;
        } catch (VoteMessageCommitException e) {
            logger.error("Unable to add message: {}", typedMessage, e);
            return false;
        }

        return true;
    }

    /**
     * Adds a cancel message
     * 
     * @param typedMessage
     * @param commitment
     * @throws CommitIdentifierException
     * @throws VoteMessageCommitException 
     */
    private void addCancelMessage(TypedJSONMessage typedMessage, FinalCommitment commitment)
            throws CommitIdentifierException, VoteMessageCommitException {
        if (typedMessage instanceof CancelMessage) {
            CancelMessage message = (CancelMessage) typedMessage;

            String serialNumber = message.getSerialNo();

            this.checkCancelSerialNumber(serialNumber);

            String boothID = message.getBoothID();
            CommitIdentifier identifier = new CommitIdentifier(commitment.getIdentifier(), boothID);

            Map<String, CancelMessage> cancelMessageMap = null;

            if (this.cancelMessages.containsKey(identifier)) {
                cancelMessageMap = this.cancelMessages.get(identifier);
                cancelMessageMap.put(serialNumber, message);
            } else {
                cancelMessageMap = new TreeMap<String, CancelMessage>(new BallotSerialNumberComparator());
                cancelMessageMap.put(serialNumber, message);
                this.cancelMessages.put(identifier, cancelMessageMap);
            }
        }
    }

    /**
     * Checks that a serial number doesn't already exist within the cancel message
     * store
     * 
     * @param serialNumber
     * @throws VoteMessageCommitException
     */
    private void checkCancelSerialNumber(String serialNumber) throws VoteMessageCommitException {
        for (CommitIdentifier identifier : this.cancelMessages.keySet()) {
            if (this.cancelMessages.get(identifier).containsKey(serialNumber)) {
                logger.warn("Should not have a cancel message already existing with the same serial number: {}",
                        serialNumber);
                resultsLogger.warn(
                        "Should not have a cancel message already existing with the same serial number: {}",
                        serialNumber);
            }
        }
    }

    /**
     * Private helper method to add a POD message
     * 
     * @param typedMessage
     * @param commitment
     * @throws CommitIdentifierException
     * @throws VoteMessageCommitException
     */
    private void addPODMessage(TypedJSONMessage typedMessage, FinalCommitment commitment)
            throws CommitIdentifierException, VoteMessageCommitException {
        if (typedMessage instanceof PODMessage) {
            PODMessage message = (PODMessage) typedMessage;

            String serialNumber = message.getSerialNo();

            this.checkPODSerialNumber(serialNumber);

            String boothID = message.getBoothID();
            CommitIdentifier identifier = new CommitIdentifier(commitment.getIdentifier(), boothID);

            Map<String, PODMessage> podMessageMap = null;

            if (this.podMessages.containsKey(identifier)) {
                podMessageMap = this.podMessages.get(identifier);
                if (podMessageMap.containsKey(serialNumber)) {
                    podMessageMap.remove(serialNumber);
                }
                podMessageMap.put(serialNumber, message);
            } else {
                podMessageMap = new TreeMap<String, PODMessage>(new BallotSerialNumberComparator());
                podMessageMap.put(serialNumber, message);
                this.podMessages.put(identifier, podMessageMap);
            }
        }
    }

    /**
     * Private helper method to add a Vote message
     * 
     * @param typedMessage
     * @param commitment
     * @throws CommitIdentifierException
     * @throws VoteMessageCommitException
     */
    private void addVoteMessage(TypedJSONMessage typedMessage, FinalCommitment commitment)
            throws CommitIdentifierException, VoteMessageCommitException {
        if (typedMessage instanceof VoteMessage) {
            VoteMessage message = (VoteMessage) typedMessage;

            String serialNumber = message.getSerialNo();

            this.checkVoteSerialNumber(serialNumber);

            String boothID = message.getBoothID();
            CommitIdentifier identifier = new CommitIdentifier(commitment.getIdentifier(), boothID);

            Map<String, VoteMessage> voteMessageMap = null;

            if (this.voteMessages.containsKey(identifier)) {
                voteMessageMap = this.voteMessages.get(identifier);
                if (voteMessageMap.containsKey(serialNumber)) {
                    voteMessageMap.remove(serialNumber);
                }
                voteMessageMap.put(serialNumber, message);
            } else {
                voteMessageMap = new TreeMap<String, VoteMessage>(new BallotSerialNumberComparator());
                voteMessageMap.put(serialNumber, message);
                this.voteMessages.put(identifier, voteMessageMap);
            }
        }
    }

    /**
     * Checks that a serial number doesn't already exist within the pod message
     * store
     * 
     * @param serialNumber
     * @throws VoteMessageCommitException
     */
    private void checkPODSerialNumber(String serialNumber) throws VoteMessageCommitException {
        for (CommitIdentifier identifier : this.podMessages.keySet()) {
            if (this.podMessages.get(identifier).containsKey(serialNumber)) {
                logger.warn("Should not have a pod message already existing with the same serial number: {}",
                        serialNumber);
                resultsLogger.warn("Should not have a pod message already existing with the same serial number: {}",
                        serialNumber);
            }
        }
    }

    /**
     * Checks that a serial number doesn't already exist within the vote message
     * store
     * 
     * @param serialNumber
     * @throws VoteMessageCommitException
     */
    private void checkVoteSerialNumber(String serialNumber) throws VoteMessageCommitException {
        for (CommitIdentifier identifier : this.voteMessages.keySet()) {
            if (this.voteMessages.get(identifier).containsKey(serialNumber)) {
                logger.warn("Should not have a vote message already existing with the same serial number: {}",
                        serialNumber);
                resultsLogger.warn(
                        "Should not have a vote message already existing with the same serial number: {}",
                        serialNumber);
            }
        }
    }

    /**
     * Getter for the vote messages
     * 
     * @return voteMessages
     */
    public Map<CommitIdentifier, Map<String, VoteMessage>> getVoteMessages() {
        return Collections.unmodifiableMap(this.voteMessages);
    }

    /**
     * Getter for the pod messages
     * 
     * @return podMessages
     */
    public Map<CommitIdentifier, Map<String, PODMessage>> getPodMessages() {
        return Collections.unmodifiableMap(this.podMessages);
    }

    /**
     * Getter for the voting processes
     * 
     * @return votingProcesses
     */
    public Map<String, VotingProcess> getVotingProcesses() {
        return Collections.unmodifiableMap(this.votingProcesses);
    }

    /**
     * Getter for the mixnet output preferences
     * 
     * @return mixOutput
     */
    public Map<RaceIdentifier, MixOutput> getMixOutputPreferences() {
        return Collections.unmodifiableMap(this.mixOutputPreferences);
    }

    /**
     * Getter for the padding point
     * 
     * @return paddingPoint
     */
    public ECPoint getPaddingPoint() {
        return this.paddingPoint;
    }

    /**
     * Getter for the encrypted padding point
     * 
     * @return encryptedPaddingPoint
     */
    public ElGamalECPoint getEncryptedPaddingPoint() {
        return this.encryptedPaddingPoint;
    }

    /**
     * Getter for the mix input data
     * 
     * @return mixInput
     */
    public Map<RaceIdentifier, List<List<ElGamalECPoint>>> getMixInput() {
        return Collections.unmodifiableMap(this.mixInput);
    }

    /**
     * Getter for the mix output data
     * 
     * @return mixOutput
     */
    public Map<RaceIdentifier, List<List<ECPoint>>> getMixOutput() {
        return Collections.unmodifiableMap(this.mixOutput);
    }

    /**
     * Getter for has race map
     * 
     * @return hasRaceMap
     */
    public boolean hasRaceMap() {
        return this.hasRaceMap;
    }

}