pub(crate) mod context_directory;
pub(crate) mod file;
pub(crate) mod file_group;
#[cfg(test)]
pub(crate) mod mock;
pub(crate) mod tally_directory;
pub use self::{
context_directory::{ContextDirectory, ContextDirectoryTrait},
tally_directory::TallyDirectoryTrait,
};
use crate::{
data_structures::{
DataStructureError, VerifierDataType, context::VerifierContextDataType,
tally::VerifierTallyDataType,
},
verification::VerificationPeriod,
};
use rust_ev_system_library::rust_ev_crypto_primitives::prelude::basic_crypto_functions::BasisCryptoError;
use std::path::{Path, PathBuf};
use tally_directory::TallyDirectory;
use thiserror::Error;
#[derive(Error, Debug)]
#[error(transparent)]
pub struct FileStructureError(#[from] FileStructureErrorImpl);
#[derive(Error, Debug)]
enum FileStructureErrorImpl {
#[error("Path is not a file {0}")]
PathNotFile(PathBuf),
#[error("Error reading data in decode data")]
ReadDataDecoding { source: Box<FileStructureError> },
#[error("IO error reading file {path}")]
IO {
path: PathBuf,
source: std::io::Error,
},
#[error("Error with {path}: {msg}")]
ReadDataStructure {
msg: &'static str,
path: PathBuf,
source: Box<DataStructureError>,
},
#[error("Path is not a directory {0}")]
PathIsNotDir(PathBuf),
#[error("Error calculating fingerprint of {path}")]
Fingerprint {
path: PathBuf,
source: BasisCryptoError,
},
#[cfg(test)]
#[error("Mock error: {0}")]
Mock(String),
}
pub struct VerificationDirectory {
context: ContextDirectory,
tally: Option<TallyDirectory>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
enum FileType {
Json,
Xml,
}
#[derive(Debug, Clone, PartialEq, Eq)]
enum FileReadMode {
Memory,
#[allow(dead_code)]
Streaming,
Cache,
}
trait GetFileNameTrait {
fn get_raw_file_name(&self) -> String;
fn get_file_name(&self, value: Option<usize>) -> String {
let s = self.get_raw_file_name();
match value {
Some(i) => s.replace("{}", &i.to_string()),
None => s,
}
}
}
pub trait VerificationDirectoryTrait {
type ContextDirType: ContextDirectoryTrait;
type TallyDirType: TallyDirectoryTrait;
fn context(&self) -> &Self::ContextDirType;
fn unwrap_tally(&self) -> &Self::TallyDirType;
fn path(&self) -> &Path;
fn path_to_string(&self) -> String {
self.path().to_str().unwrap().to_string()
}
}
pub trait CompletnessTestTrait {
fn test_completness(&self) -> Result<Vec<String>, FileStructureError>;
}
impl VerificationDirectory {
pub fn new(period: &VerificationPeriod, location: &Path) -> Self {
let context = ContextDirectory::new(location);
match period {
VerificationPeriod::Setup => VerificationDirectory {
context,
tally: None,
},
VerificationPeriod::Tally => VerificationDirectory {
context,
tally: Some(TallyDirectory::new(location)),
},
}
}
pub fn is_config(&self) -> bool {
self.tally.is_none()
}
pub fn is_tally(&self) -> bool {
self.tally.is_some()
}
pub fn is_valid(&self) -> bool {
self.is_config() != self.is_tally()
}
}
impl VerificationDirectoryTrait for VerificationDirectory {
type ContextDirType = ContextDirectory;
type TallyDirType = TallyDirectory;
fn context(&self) -> &ContextDirectory {
&self.context
}
fn unwrap_tally(&self) -> &TallyDirectory {
match &self.tally {
Some(t) => t,
None => panic!("called `unwrap_tally()` on a `Setup` value"),
}
}
fn path(&self) -> &Path {
self.context.location().parent().unwrap()
}
}
impl GetFileNameTrait for VerifierContextDataType {
fn get_raw_file_name(&self) -> String {
let s = match self {
Self::ElectionEventContextPayload => "electionEventContextPayload.json",
Self::SetupComponentPublicKeysPayload => "setupComponentPublicKeysPayload.json",
Self::ControlComponentPublicKeysPayload => "controlComponentPublicKeysPayload.{}.json",
Self::SetupComponentTallyDataPayload => "setupComponentTallyDataPayload.json",
Self::ElectionEventConfiguration => "configuration-anonymized.xml",
};
s.to_string()
}
}
impl From<&VerifierContextDataType> for FileReadMode {
fn from(value: &VerifierContextDataType) -> Self {
match value {
VerifierContextDataType::ElectionEventContextPayload => FileReadMode::Cache,
VerifierContextDataType::SetupComponentPublicKeysPayload => FileReadMode::Memory,
VerifierContextDataType::ControlComponentPublicKeysPayload => FileReadMode::Memory,
VerifierContextDataType::SetupComponentTallyDataPayload => FileReadMode::Memory,
VerifierContextDataType::ElectionEventConfiguration => FileReadMode::Memory,
}
}
}
impl From<&VerifierContextDataType> for FileType {
fn from(value: &VerifierContextDataType) -> Self {
match value {
VerifierContextDataType::ElectionEventContextPayload => FileType::Json,
VerifierContextDataType::SetupComponentPublicKeysPayload => FileType::Json,
VerifierContextDataType::ControlComponentPublicKeysPayload => FileType::Json,
VerifierContextDataType::SetupComponentTallyDataPayload => FileType::Json,
VerifierContextDataType::ElectionEventConfiguration => FileType::Xml,
}
}
}
impl GetFileNameTrait for VerifierTallyDataType {
fn get_raw_file_name(&self) -> String {
let s = match self {
Self::ECH0222 => "eCH-0222_*.xml",
Self::TallyComponentVotesPayload => "tallyComponentVotesPayload.json",
Self::TallyComponentShufflePayload => "tallyComponentShufflePayload.json",
Self::ControlComponentBallotBoxPayload => "controlComponentBallotBoxPayload_{}.json",
Self::ControlComponentShufflePayload => "controlComponentShufflePayload_{}.json",
};
s.to_string()
}
}
impl From<&VerifierTallyDataType> for FileReadMode {
fn from(value: &VerifierTallyDataType) -> Self {
match value {
VerifierTallyDataType::ECH0222 => FileReadMode::Memory,
VerifierTallyDataType::TallyComponentVotesPayload => FileReadMode::Memory,
VerifierTallyDataType::TallyComponentShufflePayload => FileReadMode::Memory,
VerifierTallyDataType::ControlComponentBallotBoxPayload => FileReadMode::Memory,
VerifierTallyDataType::ControlComponentShufflePayload => FileReadMode::Memory,
}
}
}
impl From<&VerifierTallyDataType> for FileType {
fn from(value: &VerifierTallyDataType) -> Self {
match value {
VerifierTallyDataType::ECH0222 => FileType::Xml,
VerifierTallyDataType::TallyComponentVotesPayload => FileType::Json,
VerifierTallyDataType::TallyComponentShufflePayload => FileType::Json,
VerifierTallyDataType::ControlComponentBallotBoxPayload => FileType::Json,
VerifierTallyDataType::ControlComponentShufflePayload => FileType::Json,
}
}
}
impl GetFileNameTrait for VerifierDataType {
fn get_raw_file_name(&self) -> String {
match self {
VerifierDataType::Context(t) => t.get_raw_file_name(),
VerifierDataType::Tally(t) => t.get_raw_file_name(),
}
}
}
impl From<&VerifierDataType> for FileReadMode {
fn from(value: &VerifierDataType) -> Self {
match value {
VerifierDataType::Context(t) => FileReadMode::from(t),
VerifierDataType::Tally(t) => FileReadMode::from(t),
}
}
}
impl From<&VerifierDataType> for FileType {
fn from(value: &VerifierDataType) -> Self {
match value {
VerifierDataType::Context(t) => FileType::from(t),
VerifierDataType::Tally(t) => FileType::from(t),
}
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::config::test::{
test_ballot_box_one_vote_path, test_context_verification_card_set_path,
test_datasets_context_path,
};
#[test]
fn test_context_files_exist() {
let path = test_datasets_context_path();
assert!(
path.join(
VerifierDataType::Context(VerifierContextDataType::ElectionEventContextPayload)
.get_file_name(None)
)
.exists()
);
assert!(
path.join(
VerifierDataType::Context(VerifierContextDataType::SetupComponentPublicKeysPayload)
.get_file_name(None)
)
.exists()
);
let path2 = test_context_verification_card_set_path();
assert!(
path2
.join(
VerifierDataType::Context(
VerifierContextDataType::SetupComponentTallyDataPayload
)
.get_file_name(None)
)
.exists()
);
}
#[test]
fn test_tally_files_exist() {
let path2 = test_ballot_box_one_vote_path();
assert!(
path2
.join(
VerifierDataType::Tally(VerifierTallyDataType::TallyComponentVotesPayload)
.get_file_name(None)
)
.exists()
);
assert!(
path2
.join(
VerifierDataType::Tally(VerifierTallyDataType::TallyComponentShufflePayload)
.get_file_name(None)
)
.exists()
);
}
#[test]
fn test_context_groups_exist() {
let path = test_datasets_context_path();
assert!(
path.join(
VerifierDataType::Context(
VerifierContextDataType::ControlComponentPublicKeysPayload
)
.get_file_name(Some(1))
)
.exists()
);
}
}