1use derive_builder::Builder;
2use log::debug;
3use serde::Deserialize;
4use std::{ffi::OsString, fs::File, io::Read, path::PathBuf, str::FromStr};
5
6use crate::{
7 accumulators::AccumulatorType,
8 entity::{self, EntitiesParser},
9 utils::LogOnErr,
10 DapolTree, DapolTreeError, Height, MaxLiability, MaxThreadCount, Salt, Secret,
11};
12use crate::{salt, secret};
13
14#[doc = include_str!("../examples/dapol_config_example.toml")]
22#[derive(Deserialize, Debug, Builder, PartialEq)]
72#[builder(build_fn(skip))]
73pub struct DapolConfig {
74 #[doc = include_str!("./shared_docs/accumulator_type.md")]
75 accumulator_type: AccumulatorType,
76
77 #[doc = include_str!("./shared_docs/salt_b.md")]
78 salt_b: Salt,
79
80 #[doc = include_str!("./shared_docs/salt_s.md")]
81 salt_s: Salt,
82
83 #[doc = include_str!("./shared_docs/max_liability.md")]
84 max_liability: MaxLiability,
85
86 #[doc = include_str!("./shared_docs/height.md")]
87 height: Height,
88
89 #[doc = include_str!("./shared_docs/max_thread_count.md")]
90 max_thread_count: MaxThreadCount,
91
92 #[builder(setter(custom))]
93 random_seed: Option<u64>,
94
95 #[builder(private)]
96 entities: EntityConfig,
97
98 #[builder(private)]
99 secrets: SecretsConfig,
100}
101
102use serde_with::{serde_as, DisplayFromStr};
103#[serde_as]
104#[derive(Deserialize, Debug, Clone, Default, PartialEq)]
105pub struct SecretsConfig {
106 file_path: Option<PathBuf>,
107 #[serde_as(as = "Option<DisplayFromStr>")]
108 master_secret: Option<Secret>,
109}
110
111#[derive(Deserialize, Debug, Clone, Default, PartialEq)]
112pub struct EntityConfig {
113 file_path: Option<PathBuf>,
114 num_random_entities: Option<u64>,
115}
116
117impl DapolConfigBuilder {
121 pub fn entities_file_path_opt(&mut self, path: Option<PathBuf>) -> &mut Self {
126 match &mut self.entities {
127 None => {
128 self.entities = Some(EntityConfig {
129 file_path: path,
130 num_random_entities: None,
131 })
132 }
133 Some(entities) => entities.file_path = path,
134 }
135 self
136 }
137
138 pub fn entities_file_path(&mut self, path: PathBuf) -> &mut Self {
140 self.entities_file_path_opt(Some(path))
141 }
142
143 pub fn num_random_entities_opt(&mut self, num_entities: Option<u64>) -> &mut Self {
151 match &mut self.entities {
152 None => {
153 self.entities = Some(EntityConfig {
154 file_path: None,
155 num_random_entities: num_entities,
156 })
157 }
158 Some(entities) => entities.num_random_entities = num_entities,
159 }
160 self
161 }
162
163 pub fn num_random_entities(&mut self, num_entities: u64) -> &mut Self {
168 self.num_random_entities_opt(Some(num_entities))
169 }
170
171 pub fn secrets_file_path_opt(&mut self, path: Option<PathBuf>) -> &mut Self {
176 match &mut self.secrets {
177 None => {
178 self.secrets = Some(SecretsConfig {
179 file_path: path,
180 master_secret: None,
181 })
182 }
183 Some(secrets) => secrets.file_path = path,
184 }
185 self
186 }
187
188 pub fn secrets_file_path(&mut self, path: PathBuf) -> &mut Self {
190 self.secrets_file_path_opt(Some(path))
191 }
192
193 #[doc = include_str!("./shared_docs/master_secret.md")]
195 pub fn master_secret(&mut self, master_secret: Secret) -> &mut Self {
196 match &mut self.secrets {
197 None => {
198 self.secrets = Some(SecretsConfig {
199 file_path: None,
200 master_secret: Some(master_secret),
201 })
202 }
203 Some(secrets) => secrets.master_secret = Some(master_secret),
204 }
205 self
206 }
207
208 #[doc = include_str!("./shared_docs/salt_b.md")]
209 pub fn salt_b_opt(&mut self, salt_b: Option<Salt>) -> &mut Self {
213 self.salt_b = salt_b;
214 self
215 }
216
217 #[doc = include_str!("./shared_docs/salt_s.md")]
218 pub fn salt_s_opt(&mut self, salt_s: Option<Salt>) -> &mut Self {
222 self.salt_s = salt_s;
223 self
224 }
225
226 #[cfg(any(test, feature = "testing"))]
231 pub fn random_seed(&mut self, random_seed: u64) -> &mut Self {
232 self.random_seed = Some(Some(random_seed));
233 self
234 }
235
236 #[cfg(any(test, feature = "testing"))]
237 fn get_random_seed(&self) -> Option<u64> {
238 self.random_seed.unwrap_or(None)
239 }
240
241 #[cfg(not(any(test, feature = "testing")))]
242 fn get_random_seed(&self) -> Option<u64> {
243 None
244 }
245
246 pub fn build(&self) -> Result<DapolConfig, DapolConfigBuilderError> {
248 let accumulator_type =
249 self.accumulator_type
250 .clone()
251 .ok_or(DapolConfigBuilderError::UninitializedField(
252 "accumulator_type",
253 ))?;
254
255 let entities = EntityConfig {
256 file_path: self.entities.clone().and_then(|e| e.file_path).or(None),
257 num_random_entities: self
258 .entities
259 .clone()
260 .and_then(|e| e.num_random_entities)
261 .or(None),
262 };
263
264 if entities.file_path.is_none() && entities.num_random_entities.is_none() {
265 return Err(DapolConfigBuilderError::UninitializedField("entities"));
266 }
267
268 let secrets = SecretsConfig {
269 file_path: self.secrets.clone().and_then(|e| e.file_path).or(None),
270 master_secret: self.secrets.clone().and_then(|e| e.master_secret).or(None),
271 };
272
273 if secrets.file_path.is_none() && secrets.master_secret.is_none() {
274 return Err(DapolConfigBuilderError::UninitializedField("secrets"));
275 }
276
277 let salt_b = self.salt_b.clone().unwrap_or_default();
278 let salt_s = self.salt_s.clone().unwrap_or_default();
279 let height = self.height.unwrap_or_default();
280 let max_thread_count = self.max_thread_count.unwrap_or_default();
281 let max_liability = self.max_liability.unwrap_or_default();
282 let random_seed = self.get_random_seed();
283
284 Ok(DapolConfig {
285 accumulator_type,
286 salt_b,
287 salt_s,
288 max_liability,
289 height,
290 max_thread_count,
291 entities,
292 secrets,
293 random_seed,
294 })
295 }
296}
297
298impl DapolConfig {
302 pub fn deserialize(config_file_path: PathBuf) -> Result<Self, DapolConfigError> {
318 debug!(
319 "Attempting to deserialize {:?} as a file containing DAPOL config",
320 config_file_path.clone().into_os_string()
321 );
322
323 let ext = config_file_path
324 .extension()
325 .and_then(|s| s.to_str())
326 .ok_or(DapolConfigError::UnknownFileType(
327 config_file_path.clone().into_os_string(),
328 ))?;
329
330 let mut config = match FileType::from_str(ext)? {
331 FileType::Toml => {
332 let mut buf = String::new();
333 File::open(config_file_path.clone())?.read_to_string(&mut buf)?;
334 let config: DapolConfig = toml::from_str(&buf)?;
335 config
336 }
337 };
338
339 config.entities.file_path =
340 extend_path_if_relative(config_file_path.clone(), config.entities.file_path);
341 config.secrets.file_path =
342 extend_path_if_relative(config_file_path, config.secrets.file_path);
343
344 debug!("Successfully deserialized DAPOL config file");
345
346 Ok(config)
347 }
348
349 #[cfg(any(test, feature = "testing"))]
352 pub fn parse(self) -> Result<DapolTree, DapolConfigError> {
353 debug!("Parsing config to create a new DAPOL tree: {:?}", self);
354
355 let salt_b = self.salt_b;
356 let salt_s = self.salt_s;
357
358 let entities = EntitiesParser::new()
359 .with_path_opt(self.entities.file_path)
360 .with_num_entities_opt(self.entities.num_random_entities)
361 .parse_file_or_generate_random()?;
362
363 let master_secret = if let Some(path) = self.secrets.file_path {
364 Ok(DapolConfig::parse_secrets_file(path)?)
365 } else if let Some(master_secret) = self.secrets.master_secret {
366 Ok(master_secret)
367 } else {
368 Err(DapolConfigError::CannotFindMasterSecret)
369 }?;
370
371 let dapol_tree = if let Some(random_seed) = self.random_seed {
372 DapolTree::new_with_random_seed(
373 self.accumulator_type,
374 master_secret,
375 salt_b,
376 salt_s,
377 self.max_liability,
378 self.max_thread_count,
379 self.height,
380 entities,
381 random_seed,
382 )
383 .log_on_err()?
384 } else {
385 DapolTree::new(
386 self.accumulator_type,
387 master_secret,
388 salt_b,
389 salt_s,
390 self.max_liability,
391 self.max_thread_count,
392 self.height,
393 entities,
394 )
395 .log_on_err()?
396 };
397
398 Ok(dapol_tree)
399 }
400
401 #[cfg(not(any(test, feature = "testing")))]
404 pub fn parse(self) -> Result<DapolTree, DapolConfigError> {
405 debug!("Parsing config to create a new DAPOL tree: {:?}", self);
406
407 let salt_b = self.salt_b;
408 let salt_s = self.salt_s;
409
410 let entities = EntitiesParser::new()
411 .with_path_opt(self.entities.file_path)
412 .with_num_entities_opt(self.entities.num_random_entities)
413 .parse_file_or_generate_random()?;
414
415 let master_secret = if let Some(path) = self.secrets.file_path {
416 Ok(DapolConfig::parse_secrets_file(path)?)
417 } else if let Some(master_secret) = self.secrets.master_secret {
418 Ok(master_secret)
419 } else {
420 Err(DapolConfigError::CannotFindMasterSecret)
421 }?;
422
423 Ok(DapolTree::new(
424 self.accumulator_type,
425 master_secret,
426 salt_b,
427 salt_s,
428 self.max_liability,
429 self.max_thread_count,
430 self.height,
431 entities,
432 )
433 .log_on_err()?)
434 }
435
436 fn parse_secrets_file(path: PathBuf) -> Result<Secret, SecretsParserError> {
444 debug!(
445 "Attempting to parse {:?} as a file containing secrets",
446 path
447 );
448
449 let ext = path.extension().and_then(|s| s.to_str()).ok_or(
450 SecretsParserError::UnknownFileType(path.clone().into_os_string()),
451 )?;
452
453 let master_secret = match FileType::from_str(ext)? {
454 FileType::Toml => {
455 let mut buf = String::new();
456 File::open(path)?.read_to_string(&mut buf)?;
457 let secrets: DapolSecrets = toml::from_str(&buf)?;
458 secrets.master_secret
459 }
460 };
461
462 debug!("Successfully parsed DAPOL secrets file",);
463
464 Ok(master_secret)
465 }
466}
467
468fn extend_path_if_relative(
469 leader_path: PathBuf,
470 possibly_relative_path: Option<PathBuf>,
471) -> Option<PathBuf> {
472 match possibly_relative_path {
473 Some(path) => Some(
474 path.strip_prefix("./")
475 .map(|p| p.to_path_buf())
476 .ok()
477 .and_then(|tail| leader_path.parent().map(|parent| parent.join(tail)))
478 .unwrap_or(path.clone()),
479 ),
480 None => None,
481 }
482}
483
484enum FileType {
486 Toml,
487}
488
489impl FromStr for FileType {
490 type Err = SecretsParserError;
491
492 fn from_str(ext: &str) -> Result<FileType, Self::Err> {
493 match ext {
494 "toml" => Ok(FileType::Toml),
495 _ => Err(SecretsParserError::UnsupportedFileType { ext: ext.into() }),
496 }
497 }
498}
499
500#[derive(Deserialize, Debug)]
501struct DapolSecrets {
502 master_secret: Secret,
503}
504
505#[derive(thiserror::Error, Debug)]
510pub enum DapolConfigError {
511 #[error("Entities parsing failed while trying to parse DAPOL config")]
512 EntitiesError(#[from] entity::EntitiesParserError),
513 #[error("Error parsing the master secret string")]
514 MasterSecretParseError(#[from] secret::SecretParserError),
515 #[error("Error parsing the master secret file")]
516 MasterSecretFileParseError(#[from] SecretsParserError),
517 #[error("Either master secret must be set directly, or a path to a file containing it must be given")]
518 CannotFindMasterSecret,
519 #[error("Error parsing the salt string")]
520 SaltParseError(#[from] salt::SaltParserError),
521 #[error("Tree construction failed after parsing DAPOL config")]
522 BuildError(#[from] DapolTreeError),
523 #[error("Unable to find file extension for path {0:?}")]
524 UnknownFileType(OsString),
525 #[error("The file type with extension {ext:?} is not supported")]
526 UnsupportedFileType { ext: String },
527 #[error("Error reading the file")]
528 FileReadError(#[from] std::io::Error),
529 #[error("Deserialization process failed")]
530 DeserializationError(#[from] toml::de::Error),
531}
532
533#[derive(thiserror::Error, Debug)]
534pub enum SecretsParserError {
535 #[error("Unable to find file extension for path {0:?}")]
536 UnknownFileType(OsString),
537 #[error("The file type with extension {ext:?} is not supported")]
538 UnsupportedFileType { ext: String },
539 #[error("Error reading the file")]
540 FileReadError(#[from] std::io::Error),
541 #[error("Deserialization process failed")]
542 DeserializationError(#[from] toml::de::Error),
543}
544
545#[cfg(test)]
549mod tests {
550 use crate::accumulators::Accumulator;
551 use crate::utils::test_utils::assert_err;
552
553 use super::*;
554 use std::fs::File;
555 use std::io::{BufRead, BufReader};
556 use std::path::Path;
557
558 fn dapol_config_builder_matching_example_file() -> DapolConfigBuilder {
560 let src_dir = env!("CARGO_MANIFEST_DIR");
561 let resources_dir = Path::new(&src_dir).join("examples");
562 let secrets_file_path = resources_dir.join("dapol_secrets_example.toml");
563 let entities_file_path = resources_dir.join("entities_example.csv");
564
565 let height = Height::expect_from(16u8);
566 let salt_b = Salt::from_str("salt_b").unwrap();
567 let salt_s = Salt::from_str("salt_s").unwrap();
568 let max_liability = MaxLiability::from(10_000_000u64);
569 let max_thread_count = MaxThreadCount::from(8u8);
570 let master_secret = Secret::from_str("master_secret").unwrap();
571 let num_entities = 100u64;
572
573 DapolConfigBuilder::default()
574 .accumulator_type(AccumulatorType::NdmSmt)
575 .height(height.clone())
576 .salt_b(salt_b.clone())
577 .salt_s(salt_s.clone())
578 .max_liability(max_liability.clone())
579 .max_thread_count(max_thread_count.clone())
580 .secrets_file_path(secrets_file_path.clone())
581 .master_secret(master_secret.clone())
582 .entities_file_path(entities_file_path.clone())
583 .num_random_entities(num_entities)
584 .clone()
585 }
586
587 mod creating_config {
588 use super::*;
589
590 #[test]
591 fn builder_with_all_default_values_gives_correct_config() {
592 let src_dir = env!("CARGO_MANIFEST_DIR");
599 let resources_dir = Path::new(&src_dir).join("examples");
600 let secrets_file_path = resources_dir.join("dapol_secrets_example.toml");
601 let entities_file_path = resources_dir.join("entities_example.csv");
602
603 let dapol_config = DapolConfigBuilder::default()
604 .accumulator_type(AccumulatorType::NdmSmt)
605 .secrets_file_path(secrets_file_path.clone())
606 .entities_file_path(entities_file_path.clone())
607 .build()
608 .unwrap();
609
610 assert_eq!(dapol_config.accumulator_type, AccumulatorType::NdmSmt);
612 assert_eq!(dapol_config.entities.file_path, Some(entities_file_path));
613 assert_eq!(dapol_config.secrets.file_path, Some(secrets_file_path));
614
615 assert_eq!(dapol_config.entities.num_random_entities, None);
617 assert_eq!(dapol_config.secrets.master_secret, None);
618 assert_eq!(dapol_config.max_thread_count, MaxThreadCount::default());
619 assert_eq!(dapol_config.height, Height::default());
620 assert_eq!(dapol_config.max_liability, MaxLiability::default());
621
622 assert!(dapol_config.salt_b.as_bytes().iter().any(|b| *b != 0u8));
624 assert!(dapol_config.salt_s.as_bytes().iter().any(|b| *b != 0u8));
625 }
626
627 #[test]
628 fn builder_with_no_default_values_gives_correct_config() {
629 let src_dir = env!("CARGO_MANIFEST_DIR");
630 let resources_dir = Path::new(&src_dir).join("examples");
631 let secrets_file_path = resources_dir.join("dapol_secrets_example.toml");
632 let entities_file_path = resources_dir.join("entities_example.csv");
633
634 let height = Height::expect_from(16u8);
635 let salt_b = Salt::from_str("salt_b").unwrap();
636 let salt_s = Salt::from_str("salt_s").unwrap();
637 let max_liability = MaxLiability::from(10_000_000u64);
638 let max_thread_count = MaxThreadCount::from(8u8);
639 let master_secret = Secret::from_str("master_secret").unwrap();
640 let num_entities = 100u64;
641
642 let dapol_config = dapol_config_builder_matching_example_file()
643 .build()
644 .unwrap();
645
646 assert_eq!(dapol_config.accumulator_type, AccumulatorType::NdmSmt);
647 assert_eq!(dapol_config.entities.file_path, Some(entities_file_path));
648 assert_eq!(dapol_config.secrets.file_path, Some(secrets_file_path));
649 assert_eq!(
650 dapol_config.entities.num_random_entities,
651 Some(num_entities)
652 );
653 assert_eq!(dapol_config.secrets.master_secret, Some(master_secret));
654 assert_eq!(dapol_config.max_thread_count, max_thread_count);
655 assert_eq!(dapol_config.max_liability, max_liability);
656 assert_eq!(dapol_config.height, height);
657 assert_eq!(dapol_config.salt_b, salt_b);
658 assert_eq!(dapol_config.salt_s, salt_s);
659 }
660
661 #[test]
662 fn config_file_gives_same_config_as_builder() {
663 let src_dir = env!("CARGO_MANIFEST_DIR");
664 let resources_dir = Path::new(&src_dir).join("examples");
665 let config_file_path = resources_dir.join("dapol_config_example.toml");
666
667 let dapol_config_from_file = DapolConfig::deserialize(config_file_path).unwrap();
668 let dapol_config_from_builder = dapol_config_builder_matching_example_file()
669 .build()
670 .unwrap();
671
672 assert_eq!(dapol_config_from_file, dapol_config_from_builder);
673 }
674
675 #[test]
676 fn builder_without_accumulator_type_fails() {
677 let master_secret = Secret::from_str("master_secret").unwrap();
678 let num_entities = 100u64;
679
680 let res = DapolConfigBuilder::default()
681 .master_secret(master_secret)
682 .num_random_entities(num_entities)
683 .build();
684
685 assert_err!(
686 res,
687 Err(DapolConfigBuilderError::UninitializedField(
688 "accumulator_type"
689 ))
690 );
691 }
692
693 #[test]
694 fn builder_without_secrets_fails() {
695 let num_entities = 100u64;
696
697 let res = DapolConfigBuilder::default()
698 .accumulator_type(AccumulatorType::NdmSmt)
699 .num_random_entities(num_entities)
700 .build();
701
702 assert_err!(
703 res,
704 Err(DapolConfigBuilderError::UninitializedField("secrets"))
705 );
706 }
707
708 #[test]
709 fn builder_without_entities_fails() {
710 let master_secret = Secret::from_str("master_secret").unwrap();
711
712 let res = DapolConfigBuilder::default()
713 .accumulator_type(AccumulatorType::NdmSmt)
714 .master_secret(master_secret)
715 .build();
716
717 assert_err!(
718 res,
719 Err(DapolConfigBuilderError::UninitializedField("entities"))
720 );
721 }
722
723 #[test]
724 fn fail_when_unsupproted_secrets_file_type() {
725 let this_file = std::file!();
726 let unsupported_path = PathBuf::from(this_file);
727
728 let num_entities = 100u64;
729
730 let res = DapolConfigBuilder::default()
731 .accumulator_type(AccumulatorType::NdmSmt)
732 .num_random_entities(num_entities)
733 .secrets_file_path(unsupported_path)
734 .build()
735 .unwrap()
736 .parse();
737
738 assert_err!(
739 res,
740 Err(DapolConfigError::MasterSecretFileParseError(
741 SecretsParserError::UnsupportedFileType { ext: _ }
742 ))
743 );
744 }
745
746 #[test]
747 fn fail_when_unknown_secrets_file_type() {
748 let no_file_ext = PathBuf::from("../LICENSE");
749
750 let num_entities = 100u64;
751
752 let res = DapolConfigBuilder::default()
753 .accumulator_type(AccumulatorType::NdmSmt)
754 .num_random_entities(num_entities)
755 .secrets_file_path(no_file_ext)
756 .build()
757 .unwrap()
758 .parse();
759
760 assert_err!(
761 res,
762 Err(DapolConfigError::MasterSecretFileParseError(
763 SecretsParserError::UnknownFileType(_)
764 ))
765 );
766 }
767 }
768
769 mod config_to_tree {
771 use super::*;
772
773 #[test]
774 fn parsing_config_gives_correct_tree() {
775 let src_dir = env!("CARGO_MANIFEST_DIR");
776 let resources_dir = Path::new(&src_dir).join("examples");
777 let entities_file_path = resources_dir.join("entities_example.csv");
778
779 let entities_file = File::open(entities_file_path.clone()).unwrap();
780 let num_entities = BufReader::new(entities_file).lines().count() - 1;
783
784 let height = Height::expect_from(8u8);
785 let master_secret = Secret::from_str("master_secret").unwrap();
786 let salt_b = Salt::from_str("salt_b").unwrap();
787 let salt_s = Salt::from_str("salt_s").unwrap();
788
789 let dapol_tree = DapolConfigBuilder::default()
790 .accumulator_type(AccumulatorType::NdmSmt)
791 .height(height.clone())
792 .salt_b(salt_b.clone())
793 .salt_s(salt_s.clone())
794 .master_secret(master_secret.clone())
795 .entities_file_path(entities_file_path.clone())
796 .build()
797 .unwrap()
798 .parse()
799 .unwrap();
800
801 assert_eq!(
802 dapol_tree.entity_mapping().unwrap().len(),
803 num_entities as usize
804 );
805 assert_eq!(dapol_tree.accumulator_type(), AccumulatorType::NdmSmt);
806 assert_eq!(*dapol_tree.height(), height);
807 assert_eq!(*dapol_tree.master_secret(), master_secret);
808 assert_eq!(dapol_tree.max_liability(), &MaxLiability::default());
809 assert_eq!(*dapol_tree.salt_b(), salt_b);
810 assert_eq!(*dapol_tree.salt_s(), salt_s);
811 }
812
813 #[test]
814 fn config_with_random_entities_gives_correct_tree() {
815 let height = Height::expect_from(8);
816 let num_random_entities = 10;
817 let master_secret = Secret::from_str("master_secret").unwrap();
818
819 let dapol_tree = DapolConfigBuilder::default()
820 .accumulator_type(AccumulatorType::NdmSmt)
821 .height(height)
822 .master_secret(master_secret)
823 .num_random_entities(num_random_entities)
824 .build()
825 .unwrap()
826 .parse()
827 .unwrap();
828
829 assert_eq!(
830 dapol_tree.entity_mapping().unwrap().len(),
831 num_random_entities as usize
832 );
833 }
834
835 #[test]
836 fn secrets_file_gives_same_master_secret_as_setting_directly() {
837 let src_dir = env!("CARGO_MANIFEST_DIR");
838 let resources_dir = Path::new(&src_dir).join("examples");
839 let secrets_file_path = resources_dir.join("dapol_secrets_example.toml");
840 let entities_file_path = resources_dir.join("entities_example.csv");
841 let master_secret = Secret::from_str("master_secret").unwrap();
842 let height = Height::expect_from(8u8);
843
844 let tree_from_secrets_file = DapolConfigBuilder::default()
845 .accumulator_type(AccumulatorType::NdmSmt)
846 .height(height)
847 .secrets_file_path(secrets_file_path.clone())
848 .entities_file_path(entities_file_path.clone())
849 .build()
850 .unwrap()
851 .parse()
852 .unwrap();
853
854 let tree_from_direct_secret = DapolConfigBuilder::default()
855 .accumulator_type(AccumulatorType::NdmSmt)
856 .height(height)
857 .master_secret(master_secret.clone())
858 .entities_file_path(entities_file_path.clone())
859 .build()
860 .unwrap()
861 .parse()
862 .unwrap();
863
864 assert_eq!(
865 tree_from_direct_secret.master_secret(),
866 tree_from_secrets_file.master_secret()
867 );
868 }
869
870 #[test]
871 fn secrets_file_preferred_over_setting_directly() {
872 let src_dir = env!("CARGO_MANIFEST_DIR");
873 let resources_dir = Path::new(&src_dir).join("examples");
874 let secrets_file_path = resources_dir.join("dapol_secrets_example.toml");
875 let entities_file_path = resources_dir.join("entities_example.csv");
876 let master_secret = Secret::from_str("garbage").unwrap();
877 let height = Height::expect_from(8u8);
878
879 let dapol_tree = DapolConfigBuilder::default()
880 .accumulator_type(AccumulatorType::NdmSmt)
881 .height(height)
882 .secrets_file_path(secrets_file_path.clone())
883 .master_secret(master_secret)
884 .entities_file_path(entities_file_path.clone())
885 .build()
886 .unwrap()
887 .parse()
888 .unwrap();
889
890 assert_eq!(
891 dapol_tree.master_secret(),
892 &Secret::from_str("master_secret").unwrap()
893 );
894 }
895 }
896}