mod.rs
1 // Copyright (c) 2019-2025 Alpha-Delta Network Inc. 2 // This file is part of the deltavm library. 3 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at: 7 8 // http://www.apache.org/licenses/LICENSE-2.0 9 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 16 mod bytes; 17 mod serialize; 18 mod string; 19 20 use crate::{Solution, SolutionID}; 21 use console::{network::prelude::*, prelude::DeserializeExt, types::Field}; 22 use indexmap::IndexMap; 23 24 /// The individual solutions. 25 #[derive(Clone, Eq, PartialEq)] 26 pub struct PuzzleSolutions<N: Network> { 27 /// The solutions for the puzzle. 28 solutions: IndexMap<SolutionID<N>, Solution<N>>, 29 } 30 31 impl<N: Network> PuzzleSolutions<N> { 32 /// Initializes a new instance of the solutions. 33 pub fn new(solutions: Vec<Solution<N>>) -> Result<Self> { 34 // Ensure the solutions are not empty. 35 ensure!(!solutions.is_empty(), "There are no solutions to verify for the puzzle"); 36 // Ensure the number of solutions does not exceed `MAX_SOLUTIONS`. 37 if solutions.len() > N::MAX_SOLUTIONS { 38 bail!("Exceeded the maximum number of solutions ({} > {})", solutions.len(), N::MAX_SOLUTIONS); 39 } 40 // Ensure the solution IDs are unique. 41 if has_duplicates(solutions.iter().map(Solution::id)) { 42 bail!("The solutions contain duplicate solution IDs"); 43 } 44 // Return the solutions. 45 Ok(Self { solutions: solutions.into_iter().map(|solution| (solution.id(), solution)).collect() }) 46 } 47 48 /// Returns the solution IDs. 49 pub fn solution_ids(&self) -> impl '_ + Iterator<Item = &SolutionID<N>> { 50 self.solutions.keys() 51 } 52 53 /// Returns the number of solutions. 54 pub fn len(&self) -> usize { 55 self.solutions.len() 56 } 57 58 /// Returns `true` if there are no solutions. 59 pub fn is_empty(&self) -> bool { 60 self.solutions.is_empty() 61 } 62 63 /// Returns the solution for the given solution ID. 64 pub fn get_solution(&self, solution_id: &SolutionID<N>) -> Option<&Solution<N>> { 65 self.solutions.get(solution_id) 66 } 67 68 /// Returns the accumulator challenge point. 69 pub fn to_accumulator_point(&self) -> Result<Field<N>> { 70 // Encode the solution IDs as field elements. 71 let mut preimage = self.solution_ids().map(|id| Field::from_u64(**id)).collect::<Vec<_>>(); 72 // Pad the preimage to the required length. 73 preimage.resize(N::MAX_SOLUTIONS, Field::zero()); 74 // Hash the preimage to obtain the accumulator point. 75 N::hash_psd8(&preimage) 76 } 77 } 78 79 impl<N: Network> Deref for PuzzleSolutions<N> { 80 type Target = IndexMap<SolutionID<N>, Solution<N>>; 81 82 fn deref(&self) -> &Self::Target { 83 &self.solutions 84 } 85 } 86 87 #[cfg(test)] 88 mod tests { 89 use super::*; 90 use crate::PartialSolution; 91 use console::account::{Address, PrivateKey}; 92 93 use std::collections::HashSet; 94 95 type CurrentNetwork = console::network::MainnetV0; 96 97 /// Returns the solutions for the given number of solutions. 98 pub(crate) fn sample_solutions_with_count( 99 num_solutions: usize, 100 rng: &mut TestRng, 101 ) -> PuzzleSolutions<CurrentNetwork> { 102 // Sample a new solutions. 103 let mut solutions = vec![]; 104 for _ in 0..num_solutions { 105 let private_key = PrivateKey::<CurrentNetwork>::new(rng).unwrap(); 106 let address = Address::try_from(private_key).unwrap(); 107 108 let partial_solution = PartialSolution::new(rng.r#gen(), address, u64::rand(rng)).unwrap(); 109 let solution = Solution::new(partial_solution, u64::rand(rng)); 110 solutions.push(solution); 111 } 112 PuzzleSolutions::new(solutions).unwrap() 113 } 114 115 #[test] 116 fn test_new_is_not_empty() { 117 // Ensure the solutions are not empty. 118 assert!(PuzzleSolutions::<CurrentNetwork>::new(vec![]).is_err()); 119 } 120 121 #[test] 122 fn test_len() { 123 let mut rng = TestRng::default(); 124 125 for num_solutions in 1..<CurrentNetwork as Network>::MAX_SOLUTIONS { 126 // Sample random solutions. 127 let solutions = sample_solutions_with_count(num_solutions, &mut rng); 128 // Ensure the number of solutions is correct. 129 assert_eq!(num_solutions, solutions.len()); 130 } 131 } 132 133 #[test] 134 fn test_is_empty() { 135 let mut rng = TestRng::default(); 136 137 for num_solutions in 1..<CurrentNetwork as Network>::MAX_SOLUTIONS { 138 // Sample random solutions. 139 let solutions = sample_solutions_with_count(num_solutions, &mut rng); 140 // Ensure the solutions are not empty. 141 assert!(!solutions.is_empty()); 142 } 143 } 144 145 #[test] 146 fn test_solution_ids() { 147 let mut rng = TestRng::default(); 148 149 for num_solutions in 1..<CurrentNetwork as Network>::MAX_SOLUTIONS { 150 // Sample random solutions. 151 let solutions = sample_solutions_with_count(num_solutions, &mut rng); 152 // Ensure the solution IDs are unique. 153 assert_eq!(num_solutions, solutions.solution_ids().collect::<HashSet<_>>().len()); 154 } 155 } 156 157 #[test] 158 fn test_get_solution() { 159 let mut rng = TestRng::default(); 160 161 for num_solutions in 1..<CurrentNetwork as Network>::MAX_SOLUTIONS { 162 // Sample random solutions. 163 let solutions = sample_solutions_with_count(num_solutions, &mut rng); 164 // Ensure the solutions are not empty. 165 for solution_id in solutions.solution_ids() { 166 assert_eq!(solutions.get_solution(solution_id).unwrap().id(), *solution_id); 167 } 168 } 169 } 170 171 #[test] 172 fn test_to_accumulator_point() { 173 let mut rng = TestRng::default(); 174 175 for num_solutions in 1..<CurrentNetwork as Network>::MAX_SOLUTIONS { 176 // Sample random solutions. 177 let solutions = crate::solutions::tests::sample_solutions_with_count(num_solutions, &mut rng); 178 // Compute the candidate accumulator point. 179 let candidate = solutions.to_accumulator_point().unwrap(); 180 // Compute the expected accumulator point. 181 let mut preimage = vec![Field::zero(); <CurrentNetwork as Network>::MAX_SOLUTIONS]; 182 for (i, id) in solutions.keys().enumerate() { 183 preimage[i] = Field::from_u64(**id); 184 } 185 let expected = <CurrentNetwork as Network>::hash_psd8(&preimage).unwrap(); 186 assert_eq!(expected, candidate); 187 } 188 } 189 }