snarkvm_ledger_puzzle/solutions/
mod.rsmod bytes;
mod serialize;
mod string;
use crate::{Solution, SolutionID};
use console::{network::prelude::*, prelude::DeserializeExt, types::Field};
use indexmap::IndexMap;
#[derive(Clone, Eq, PartialEq)]
pub struct PuzzleSolutions<N: Network> {
solutions: IndexMap<SolutionID<N>, Solution<N>>,
}
impl<N: Network> PuzzleSolutions<N> {
pub fn new(solutions: Vec<Solution<N>>) -> Result<Self> {
ensure!(!solutions.is_empty(), "There are no solutions to verify for the puzzle");
if solutions.len() > N::MAX_SOLUTIONS {
bail!("Exceeded the maximum number of solutions ({} > {})", solutions.len(), N::MAX_SOLUTIONS);
}
if has_duplicates(solutions.iter().map(Solution::id)) {
bail!("The solutions contain duplicate solution IDs");
}
Ok(Self { solutions: solutions.into_iter().map(|solution| (solution.id(), solution)).collect() })
}
pub fn solution_ids(&self) -> impl '_ + Iterator<Item = &SolutionID<N>> {
self.solutions.keys()
}
pub fn len(&self) -> usize {
self.solutions.len()
}
pub fn is_empty(&self) -> bool {
self.solutions.is_empty()
}
pub fn get_solution(&self, solution_id: &SolutionID<N>) -> Option<&Solution<N>> {
self.solutions.get(solution_id)
}
pub fn to_accumulator_point(&self) -> Result<Field<N>> {
let mut preimage = self.solution_ids().map(|id| Field::from_u64(**id)).collect::<Vec<_>>();
preimage.resize(N::MAX_SOLUTIONS, Field::zero());
N::hash_psd8(&preimage)
}
}
impl<N: Network> Deref for PuzzleSolutions<N> {
type Target = IndexMap<SolutionID<N>, Solution<N>>;
fn deref(&self) -> &Self::Target {
&self.solutions
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::PartialSolution;
use console::account::{Address, PrivateKey};
use std::collections::HashSet;
type CurrentNetwork = console::network::MainnetV0;
pub(crate) fn sample_solutions_with_count(
num_solutions: usize,
rng: &mut TestRng,
) -> PuzzleSolutions<CurrentNetwork> {
let mut solutions = vec![];
for _ in 0..num_solutions {
let private_key = PrivateKey::<CurrentNetwork>::new(rng).unwrap();
let address = Address::try_from(private_key).unwrap();
let partial_solution = PartialSolution::new(rng.gen(), address, u64::rand(rng)).unwrap();
let solution = Solution::new(partial_solution, u64::rand(rng));
solutions.push(solution);
}
PuzzleSolutions::new(solutions).unwrap()
}
#[test]
fn test_new_is_not_empty() {
assert!(PuzzleSolutions::<CurrentNetwork>::new(vec![]).is_err());
}
#[test]
fn test_len() {
let mut rng = TestRng::default();
for num_solutions in 1..<CurrentNetwork as Network>::MAX_SOLUTIONS {
let solutions = sample_solutions_with_count(num_solutions, &mut rng);
assert_eq!(num_solutions, solutions.len());
}
}
#[test]
fn test_is_empty() {
let mut rng = TestRng::default();
for num_solutions in 1..<CurrentNetwork as Network>::MAX_SOLUTIONS {
let solutions = sample_solutions_with_count(num_solutions, &mut rng);
assert!(!solutions.is_empty());
}
}
#[test]
fn test_solution_ids() {
let mut rng = TestRng::default();
for num_solutions in 1..<CurrentNetwork as Network>::MAX_SOLUTIONS {
let solutions = sample_solutions_with_count(num_solutions, &mut rng);
assert_eq!(num_solutions, solutions.solution_ids().collect::<HashSet<_>>().len());
}
}
#[test]
fn test_get_solution() {
let mut rng = TestRng::default();
for num_solutions in 1..<CurrentNetwork as Network>::MAX_SOLUTIONS {
let solutions = sample_solutions_with_count(num_solutions, &mut rng);
for solution_id in solutions.solution_ids() {
assert_eq!(solutions.get_solution(solution_id).unwrap().id(), *solution_id);
}
}
}
#[test]
fn test_to_accumulator_point() {
let mut rng = TestRng::default();
for num_solutions in 1..<CurrentNetwork as Network>::MAX_SOLUTIONS {
let solutions = crate::solutions::tests::sample_solutions_with_count(num_solutions, &mut rng);
let candidate = solutions.to_accumulator_point().unwrap();
let mut preimage = vec![Field::zero(); <CurrentNetwork as Network>::MAX_SOLUTIONS];
for (i, id) in solutions.keys().enumerate() {
preimage[i] = Field::from_u64(**id);
}
let expected = <CurrentNetwork as Network>::hash_psd8(&preimage).unwrap();
assert_eq!(expected, candidate);
}
}
}