1use crate::hash::{hash_leaf, hash_node_ref, NodeHash, NULL_HASH};
2use crate::node::{is_leaf_node, is_wrapper_node};
3use crate::proof::{Proof, ProofBranch, ProofHash, ProofNode};
4use crate::tally::{combine_tally, has_one_vote, TallyList};
5use crate::utilstring::to_hex;
6use crate::{NodeRef, Validation, VoteReference};
7use sha2::{Digest, Sha256};
8use std::collections::HashSet;
9
10#[derive(Clone, Debug, PartialEq, Copy)]
12pub enum TraverseDirection {
13 Left = 0,
15 Right = 1,
17}
18
19pub enum CollapseBranchConstrains {
21 None = 0,
23 RejectLeftPath = 1,
28 RejectRightPath = 2,
33}
34
35pub fn is_vote_in_proof(vote: &VoteReference, proof: &Proof) -> bool {
39 if let Some(leaf_branch) = proof.branches.get(0) {
40 match leaf_branch {
41 (None, None) => false,
42 (Some(l), None) => &l.0 == vote,
43 (Some(l), Some(r)) => &l.0 == vote || &r.0 == vote,
44 (None, Some(r)) => &r.0 == vote,
45 }
46 } else {
47 false
48 }
49}
50
51pub fn are_neighboors(
85 left: (&VoteReference, &Proof),
86 right: (&VoteReference, &Proof),
87 v: &Validation,
88) -> Result<bool, String> {
89 let (left_vote, left_proof) = left;
90 let (right_vote, right_proof) = right;
91
92 if left_vote == right_vote {
93 return Ok(false);
94 }
95
96 if !is_vote_in_proof(left_vote, left_proof) {
97 return Err("Left vote reference does not belong to proof".to_string());
98 }
99 if !is_vote_in_proof(right_vote, right_proof) {
100 return Err("Right vote reference does not belong to proof".to_string());
101 }
102
103 if is_vote_in_proof(left_vote, right_proof) {
106 if !is_vote_in_proof(right_vote, left_proof) {
107 return Err("If left vote reference is in right proof, then right vote refrence should be in left proof".to_string());
108 }
109 return Ok(true);
110 }
111
112 if left_proof.branches.len() != right_proof.branches.len() {
116 return Err(
117 "Expected the branch length of left and right proof to be the same".to_string(),
118 );
119 }
120
121 let ((left_merkle_root, _), left_directions) = collapse_branches(
122 left_proof.branches.clone(),
123 &CollapseBranchConstrains::None,
124 v,
125 )?;
126 let ((right_merkle_root, _), right_directions) = collapse_branches(
127 right_proof.branches.clone(),
128 &CollapseBranchConstrains::None,
129 v,
130 )?;
131
132 if left_merkle_root != right_merkle_root {
133 return Err(format!(
134 "Merkle root hash for proofs don't match ({} != {})",
135 to_hex(&left_merkle_root)?,
136 to_hex(&right_merkle_root)?
137 ));
138 }
139
140 let left_index = find_node_index(
141 left_vote,
142 left_proof.branches.get(0).ok_or("Empty proof")?,
143 &left_directions,
144 )?;
145 let right_index = find_node_index(
146 right_vote,
147 right_proof.branches.get(0).ok_or("Empty proof")?,
148 &right_directions,
149 )?;
150
151 Ok(left_index + 1 == right_index || left_index != 0 && left_index - 1 == right_index)
152}
153
154pub fn collapse_branches(
188 mut branches: Vec<ProofBranch>,
189 constraints: &CollapseBranchConstrains,
190 v: &Validation,
191) -> Result<(ProofNode, Vec<TraverseDirection>), String> {
192 if branches.is_empty() {
193 return Err("Can't collapse empty branches".to_string());
194 }
195 let has_leaf = branches.len() == 1;
196
197 if has_leaf {
198 let branch = derive_wrapper_branch(&branches[0], v)?;
199 let dir = if branch.1.as_ref().unwrap().0 == *NULL_HASH.as_array() {
200 TraverseDirection::Left
201 } else {
202 TraverseDirection::Right
205 };
206 return Ok((
207 tally_and_hash(
208 &branch.0.ok_or("Invalid leaf branch")?,
209 &branch.1.ok_or("Invalid leaf branch")?,
210 )?,
211 vec![dir],
212 ));
213 };
214
215 let (proof_left, proof_right) = branches.pop().unwrap();
216
217 if proof_left.is_none() && proof_right.is_none() {
218 return Err("Both left and right nodes cannot be None".to_string());
219 }
220
221 if proof_left.is_some() && proof_right.is_some() {
222 return Err("Both left and right cannot exist for non-leaf branches".to_string());
223 }
224
225 let mut directions: Vec<TraverseDirection> = vec![];
226 let left = if let Some(l) = proof_left {
227 l
228 } else {
229 if matches!(constraints, CollapseBranchConstrains::RejectLeftPath) {
230 let (hash, _) = proof_right.as_ref().ok_or("Invalid right proof")?;
241 if hash != NULL_HASH.as_array() {
242 return Err("Proof is constrained from collapsing to the left".to_string());
243 }
244 }
245 let (n, d) = collapse_branches(branches.clone(), constraints, v)?;
246 directions = [vec![TraverseDirection::Left], d].concat();
247 n
248 };
249 let right = if let Some(r) = proof_right {
250 r
251 } else {
252 if matches!(constraints, CollapseBranchConstrains::RejectRightPath) {
253 return Err("Proof is constrained from collapsing to the right".to_string());
254 }
255 let (n, d) = collapse_branches(branches, constraints, v)?;
256 directions = [vec![TraverseDirection::Right], d].concat();
257 n
258 };
259 Ok((tally_and_hash(&left, &right)?, directions))
260}
261
262pub fn derive_wrapper_branch(branch: &ProofBranch, v: &Validation) -> Result<ProofBranch, String> {
276 let (left_proof_hash, left_tally) = branch.0.as_ref().ok_or("Invalid left leaf")?;
277 let (right_proof_hash, right_tally) = branch.1.as_ref().ok_or("Invalid right leaf")?;
278
279 let left_tally = match left_tally {
280 None => Err("Invalid proof, no tally on left node.".to_string()),
281 Some(t) => {
282 if !matches!(v, Validation::Relaxed) && !has_one_vote(t) {
283 Err("Invalid proof. Left node should have exactly one vote.".to_string())
284 } else {
285 Ok(t)
286 }
287 }
288 }?;
289 if right_proof_hash != NULL_HASH.as_array()
290 && !has_one_vote(right_tally.as_ref().unwrap_or(&vec![]))
291 {
292 return Err(
293 "Invalid proof. Right node should be null or have exactly one vote.".to_string(),
294 );
295 }
296
297 let left_hash = derive_wrapper_hash(&hash_leaf(left_proof_hash, left_tally), left_tally);
300 let right_hash = if right_proof_hash == NULL_HASH.as_array() {
301 *NULL_HASH.as_array()
302 } else {
303 derive_wrapper_hash(
304 &hash_leaf(right_proof_hash, right_tally.as_ref().unwrap()),
305 right_tally.as_ref().unwrap(),
306 )
307 };
308
309 Ok((
310 Some((left_hash, Some(left_tally.clone()))),
311 Some((right_hash, right_tally.clone())),
312 ))
313}
314
315pub fn derive_wrapper_hash(hash: &NodeHash, tally: &[u32]) -> ProofHash {
335 let mut hasher = Sha256::new();
336 tally
337 .iter()
338 .map(|t| t.to_be_bytes())
339 .for_each(|b| hasher.update(b));
340 hasher.update(hash.as_slice());
341 hasher
342 .finalize()
343 .as_slice()
344 .try_into()
345 .expect("Expected hash to be 32 bytes")
346}
347
348pub fn directions_to_node_index(directions: &[TraverseDirection]) -> usize {
383 let mut index: usize = 0;
384 for dir in directions.iter() {
385 index <<= 1;
386 index |= *dir as usize;
387 }
388 index
389}
390
391pub fn create_merkle_proof(path: &[NodeRef], v: &Validation) -> Result<Proof, String> {
412 let mut proof = Proof::default();
413
414 let mut added_nodes: HashSet<ProofHash> = HashSet::new();
415
416 for b in path.iter().rev() {
417 if is_leaf_node(b) || is_wrapper_node(b) {
418 continue;
419 }
420 if let Some(h) = hash_node_ref(b, v)? {
421 added_nodes.insert(*h.0.as_array());
424 }
425
426 let none_if_added = |node: Option<(NodeHash, Option<TallyList>)>| {
427 if let Some((hash, tally)) = node {
428 if added_nodes.contains(hash.as_array()) {
429 None
430 } else {
431 Some((*hash.as_array(), tally))
432 }
433 } else {
434 None
435 }
436 };
437
438 let leaf_to_proof_node = |leaf: &NodeRef| -> Result<Option<ProofNode>, String> {
439 let leaf = leaf.as_ref().ok_or("Missing leaf node")?;
440 let vote = leaf
441 .vote
442 .as_ref()
443 .ok_or("Invalid leaf node, vote missing")?;
444
445 Ok(Some((vote.0, Some(vote.1.clone()))))
448 };
449
450 let b = b.as_ref().ok_or("None node in path")?;
451 let left = if is_wrapper_node(&b.left) {
452 leaf_to_proof_node(&b.left.as_ref().unwrap().left)?
453 } else {
454 none_if_added(hash_node_ref(&b.left, v)?)
455 };
456 let right = if is_wrapper_node(&b.right) {
457 leaf_to_proof_node(&b.right.as_ref().unwrap().left)?
458 } else {
459 none_if_added(hash_node_ref(&b.right, v)?)
460 };
461
462 proof.branches.push((left, right))
463 }
464 Ok(proof)
465}
466
467pub fn find_node_index(
498 reference: &VoteReference,
499 leaf_branch: &ProofBranch,
500 directions: &[TraverseDirection],
501) -> Result<usize, String> {
502 let directions_index = directions_to_node_index(directions);
503
504 let (left, right) = leaf_branch;
507 let left = left.as_ref().ok_or("Left proof node missing")?;
508 let right = right.as_ref().ok_or("Right proof node missing")?;
509
510 if &right.0 != NULL_HASH.as_array() && &left.0 == reference {
511 Ok(directions_index - 1)
512 } else {
513 Ok(directions_index)
514 }
515}
516
517pub fn tally_and_hash(left: &ProofNode, right: &ProofNode) -> Result<ProofNode, String> {
519 let mut hasher = Sha256::new();
520 let tally = combine_tally(&left.1, &right.1)?;
521 tally
522 .iter()
523 .map(|t| t.to_be_bytes())
524 .for_each(|b| hasher.update(b));
525
526 hasher.update(left.0.as_slice());
527 hasher.update(right.0.as_slice());
528 let hash: [u8; 32] = hasher
529 .finalize()
530 .as_slice()
531 .try_into()
532 .expect("Expected hash to be 32 bytes");
533
534 Ok((hash, Some(tally)))
535}
536
537#[cfg(test)]
538mod tests {
539 use super::*;
540 use crate::generate::generate_tree;
541 use crate::navigate::find_node_by_vote_reference;
542 use crate::proof::votecount::create_proof_of_vote_count;
543
544 #[test]
545 fn test_are_neighboors() {
546 let a_vote: VoteReference = [0xaa; 32];
556 let b_vote: VoteReference = [0xbb; 32];
557 let c_vote: VoteReference = [0xcc; 32];
558
559 let root = generate_tree(
560 vec![
561 (a_vote, vec![1, 0]),
562 (b_vote, vec![1, 0]),
563 (c_vote, vec![0, 1]),
564 ],
565 true,
566 )
567 .unwrap();
568
569 let v = &Validation::Strict;
570 let a_proof =
571 create_merkle_proof(&find_node_by_vote_reference(&root, &a_vote).unwrap(), v).unwrap();
572 let b_proof =
573 create_merkle_proof(&find_node_by_vote_reference(&root, &b_vote).unwrap(), v).unwrap();
574 let c_proof =
575 create_merkle_proof(&find_node_by_vote_reference(&root, &c_vote).unwrap(), v).unwrap();
576
577 assert!(are_neighboors((&a_vote, &a_proof), (&b_vote, &b_proof), v).unwrap());
578 assert!(!are_neighboors((&a_vote, &a_proof), (&c_vote, &c_proof), v).unwrap());
579 assert!(are_neighboors((&b_vote, &b_proof), (&c_vote, &c_proof), v).unwrap());
580
581 let err = are_neighboors((&a_vote, &a_proof), (&c_vote, &a_proof), v);
582 assert_eq!(
583 err,
584 Err("Right vote reference does not belong to proof".to_string())
585 );
586 }
587
588 #[test]
589 fn test_create_merkle_proof() {
590 let root = generate_tree(
600 vec![
601 ([0xaa; 32], vec![1, 0]),
602 ([0xbb; 32], vec![1, 0]),
603 ([0xcc; 32], vec![0, 1]),
604 ],
605 true,
606 )
607 .unwrap();
608 let a = root.clone();
609 let b = a.as_ref().unwrap().left.clone();
610 let c = a.as_ref().unwrap().right.clone();
611 let d = b.as_ref().unwrap().left.clone();
612 let v1 = d.as_ref().unwrap().left.clone();
613 let path = [a, b, d, v1.clone()];
614 let proof = create_merkle_proof(&path, &Validation::Strict).unwrap();
615
616 assert_eq!(proof.branches.len(), 2);
620 let (proof_v1, proof_v2) = &proof.branches[0];
621 assert_eq!(proof_v1.as_ref().unwrap(), &([0xaa; 32], Some(vec![1, 0])));
622 assert_eq!(proof_v2.as_ref().unwrap(), &([0xbb; 32], Some(vec![1, 0])));
623
624 let (left, proof_c) = &proof.branches[1];
625 assert!(left.is_none());
626 let (c_hash, c_tally) = hash_node_ref(&c, &Validation::Strict).unwrap().unwrap();
627 assert_eq!(proof_c, &Some((*c_hash.as_array(), c_tally)));
628 }
629
630 #[test]
631 fn test_collapse_branches_directions() {
632 let root = generate_tree(
646 vec![
647 ([0xaa; 32], vec![1, 0]),
648 ([0xbb; 32], vec![0, 1]),
649 ([0xcc; 32], vec![0, 1]),
650 ([0xdd; 32], vec![1, 0]),
651 ([0xee; 32], vec![1, 0]),
652 ],
653 true,
654 )
655 .unwrap();
656 let proof = create_proof_of_vote_count(&root, &Validation::Strict).unwrap();
657
658 let (_, directions) = collapse_branches(
659 proof.branches,
660 &CollapseBranchConstrains::RejectLeftPath,
661 &Validation::Strict,
662 )
663 .unwrap();
664
665 assert_eq!(
666 directions,
667 vec![
668 TraverseDirection::Right,
669 TraverseDirection::Left,
670 TraverseDirection::Left
671 ]
672 );
673 }
674
675 #[test]
676 fn test_directions_to_node_index() {
677 let root = generate_tree(
678 vec![
679 ([0xaa; 32], vec![1, 0]),
680 ([0xbb; 32], vec![0, 1]),
681 ([0xcc; 32], vec![0, 1]),
682 ([0xdd; 32], vec![1, 0]),
683 ([0xee; 32], vec![1, 0]),
684 ],
685 true,
686 )
687 .unwrap();
688 let proof = create_proof_of_vote_count(&root, &Validation::Strict).unwrap();
689
690 let (_, directions) = collapse_branches(
691 proof.branches,
692 &CollapseBranchConstrains::RejectLeftPath,
693 &Validation::Strict,
694 )
695 .unwrap();
696
697 assert_eq!(directions_to_node_index(&directions), 4);
698 }
699
700 #[test]
701 fn test_collapse_branches_constraints() {
702 let tree = generate_tree(
713 vec![
714 ([0xaa; 32], vec![1, 0]),
715 ([0xbb; 32], vec![1, 0]),
716 ([0xcc; 32], vec![0, 1]),
717 ],
718 true,
719 )
720 .unwrap();
721 let v = &Validation::Strict;
722 let root_hash_and_tally = hash_node_ref(&tree, v).unwrap().unwrap();
723 let expected = (*root_hash_and_tally.0.as_array(), root_hash_and_tally.1);
724
725 let left_most_node = find_node_by_vote_reference(&tree, &[0xaa; 32]).unwrap();
726 let middle_node = find_node_by_vote_reference(&tree, &[0xbb; 32]).unwrap();
727 let right_most_node = find_node_by_vote_reference(&tree, &[0xcc; 32]).unwrap();
728
729 let left_proof = create_merkle_proof(&left_most_node, v).unwrap();
730 let middle_proof = create_merkle_proof(&middle_node, v).unwrap();
731 let right_proof = create_merkle_proof(&right_most_node, &v).unwrap();
732
733 assert_eq!(
735 expected,
736 collapse_branches(
737 left_proof.branches.clone(),
738 &CollapseBranchConstrains::None,
739 &v
740 )
741 .unwrap()
742 .0
743 );
744
745 assert_eq!(
746 Err("Proof is constrained from collapsing to the left".to_string()),
747 collapse_branches(
748 left_proof.branches.clone(),
749 &CollapseBranchConstrains::RejectLeftPath,
750 &v
751 )
752 );
753
754 assert_eq!(
755 expected,
756 collapse_branches(
757 left_proof.branches,
758 &CollapseBranchConstrains::RejectRightPath,
759 &v
760 )
761 .unwrap()
762 .0
763 );
764
765 assert_eq!(
767 expected,
768 collapse_branches(
769 middle_proof.branches.clone(),
770 &CollapseBranchConstrains::None,
771 &v
772 )
773 .unwrap()
774 .0
775 );
776
777 assert_eq!(
778 Err("Proof is constrained from collapsing to the left".to_string()),
779 collapse_branches(
780 middle_proof.branches.clone(),
781 &CollapseBranchConstrains::RejectLeftPath,
782 &v
783 )
784 );
785
786 assert_eq!(
788 expected,
789 collapse_branches(
790 middle_proof.branches,
791 &CollapseBranchConstrains::RejectRightPath,
792 &v
793 )
794 .unwrap()
795 .0
796 );
797
798 assert_eq!(
800 expected,
801 collapse_branches(
802 right_proof.branches.clone(),
803 &CollapseBranchConstrains::None,
804 &v
805 )
806 .unwrap()
807 .0
808 );
809
810 assert_eq!(
811 expected,
812 collapse_branches(
813 right_proof.branches.clone(),
814 &CollapseBranchConstrains::RejectLeftPath,
815 &v
816 )
817 .unwrap()
818 .0
819 );
820
821 assert_eq!(
822 Err("Proof is constrained from collapsing to the right".to_string()),
823 collapse_branches(
824 right_proof.branches,
825 &CollapseBranchConstrains::RejectRightPath,
826 &v
827 )
828 );
829 }
830
831 #[test]
832 fn test_find_node_index() {
833 let root = generate_tree(
843 vec![
844 ([0xaa; 32], vec![1, 0]),
845 ([0xbb; 32], vec![1, 0]),
846 ([0xcc; 32], vec![0, 1]),
847 ],
848 true,
849 )
850 .unwrap();
851 let v = &Validation::Strict;
852 let a_proof =
853 create_merkle_proof(&find_node_by_vote_reference(&root, &[0xaa; 32]).unwrap(), v)
854 .unwrap();
855 let b_proof =
856 create_merkle_proof(&find_node_by_vote_reference(&root, &[0xbb; 32]).unwrap(), v)
857 .unwrap();
858 let c_proof =
859 create_merkle_proof(&find_node_by_vote_reference(&root, &[0xcc; 32]).unwrap(), v)
860 .unwrap();
861
862 let (_, a_directions) =
863 collapse_branches(a_proof.branches.clone(), &CollapseBranchConstrains::None, v)
864 .unwrap();
865 let (_, b_directions) =
866 collapse_branches(b_proof.branches.clone(), &CollapseBranchConstrains::None, v)
867 .unwrap();
868 let (_, c_directions) =
869 collapse_branches(c_proof.branches.clone(), &CollapseBranchConstrains::None, v)
870 .unwrap();
871
872 assert_eq!(
873 0,
874 find_node_index(&[0xaa; 32], &a_proof.branches[0], &a_directions).unwrap()
875 );
876 assert_eq!(
877 1,
878 find_node_index(&[0xbb; 32], &b_proof.branches[0], &b_directions).unwrap()
879 );
880 assert_eq!(
881 2,
882 find_node_index(&[0xcc; 32], &c_proof.branches[0], &c_directions).unwrap()
883 );
884 }
885}