From 53460404bab23f5ae22d9c70d64d052a11d28107 Mon Sep 17 00:00:00 2001 From: FiveMovesAhead Date: Thu, 15 Aug 2024 13:50:30 +0800 Subject: [PATCH] Use all 512 bits of keccak512 in seed & challenge generation --- .../src/benchmarker/cuda_run_benchmark.rs | 10 ++-- .../src/benchmarker/run_benchmark.rs | 12 ++--- tig-challenges/src/knapsack.rs | 19 ++++---- tig-challenges/src/lib.rs | 46 +++++++++++++------ tig-challenges/src/satisfiability.rs | 24 +++++----- tig-challenges/src/vector_search.rs | 24 ++++------ tig-challenges/src/vehicle_routing.rs | 26 +++++++---- tig-structs/src/core.rs | 10 ++-- tig-utils/src/hash.rs | 18 +++++--- tig-worker/src/worker.rs | 22 ++++----- 10 files changed, 120 insertions(+), 91 deletions(-) diff --git a/tig-benchmarker/src/benchmarker/cuda_run_benchmark.rs b/tig-benchmarker/src/benchmarker/cuda_run_benchmark.rs index 6adadb1c..1e36ed97 100644 --- a/tig-benchmarker/src/benchmarker/cuda_run_benchmark.rs +++ b/tig-benchmarker/src/benchmarker/cuda_run_benchmark.rs @@ -84,7 +84,7 @@ pub async fn execute( yield_now().await; last_yield = now; } - let seed = job.settings.calc_seed(nonce); + let seeds = job.settings.calc_seeds(nonce); let skip = match job.settings.challenge_id.as_str() { "c001" => { type CudaSolveChallengeFn = @@ -3121,7 +3121,7 @@ pub async fn execute( .clone(); let challenge = tig_challenges::c001::Challenge::cuda_generate_instance_from_vec( - seed, + seeds, &job.settings.difficulty, &dev, challenge_cuda_funcs, @@ -6176,7 +6176,7 @@ pub async fn execute( .clone(); let challenge = tig_challenges::c002::Challenge::cuda_generate_instance_from_vec( - seed, + seeds, &job.settings.difficulty, &dev, challenge_cuda_funcs, @@ -9231,7 +9231,7 @@ pub async fn execute( .clone(); let challenge = tig_challenges::c003::Challenge::cuda_generate_instance_from_vec( - seed, + seeds, &job.settings.difficulty, &dev, challenge_cuda_funcs, @@ -12286,7 +12286,7 @@ pub async fn execute( .clone(); let challenge = tig_challenges::c004::Challenge::cuda_generate_instance_from_vec( - seed, + seeds, &job.settings.difficulty, &dev, challenge_cuda_funcs, diff --git a/tig-benchmarker/src/benchmarker/run_benchmark.rs b/tig-benchmarker/src/benchmarker/run_benchmark.rs index 0d4c26f1..b8811dea 100644 --- a/tig-benchmarker/src/benchmarker/run_benchmark.rs +++ b/tig-benchmarker/src/benchmarker/run_benchmark.rs @@ -32,7 +32,7 @@ pub async fn execute( yield_now().await; last_yield = now; } - let seed = job.settings.calc_seed(nonce); + let seeds = job.settings.calc_seeds(nonce); let skip = match job.settings.challenge_id.as_str() { "c001" => { type SolveChallengeFn = @@ -3042,7 +3042,7 @@ pub async fn execute( Some(solve_challenge) => { let challenge = tig_challenges::c001::Challenge::generate_instance_from_vec( - seed, + seeds, &job.settings.difficulty, ) .unwrap(); @@ -6064,7 +6064,7 @@ pub async fn execute( Some(solve_challenge) => { let challenge = tig_challenges::c002::Challenge::generate_instance_from_vec( - seed, + seeds, &job.settings.difficulty, ) .unwrap(); @@ -9086,7 +9086,7 @@ pub async fn execute( Some(solve_challenge) => { let challenge = tig_challenges::c003::Challenge::generate_instance_from_vec( - seed, + seeds, &job.settings.difficulty, ) .unwrap(); @@ -9103,7 +9103,7 @@ pub async fn execute( "c004" => { let challenge = tig_challenges::c004::Challenge::generate_instance_from_vec( - seed, + seeds, &job.settings.difficulty, ) .unwrap(); @@ -12114,7 +12114,7 @@ pub async fn execute( Some(solve_challenge) => { let challenge = tig_challenges::c004::Challenge::generate_instance_from_vec( - seed, + seeds, &job.settings.difficulty, ) .unwrap(); diff --git a/tig-challenges/src/knapsack.rs b/tig-challenges/src/knapsack.rs index ab206850..461c96d8 100644 --- a/tig-challenges/src/knapsack.rs +++ b/tig-challenges/src/knapsack.rs @@ -1,5 +1,6 @@ +use crate::RngArray; use anyhow::{anyhow, Result}; -use rand::{rngs::StdRng, Rng, SeedableRng}; +use rand::Rng; use serde::{Deserialize, Serialize}; use serde_json::{from_value, Map, Value}; use std::collections::HashSet; @@ -47,7 +48,7 @@ impl TryFrom> for Solution { #[derive(Serialize, Deserialize, Debug)] pub struct Challenge { - pub seed: u64, + pub seeds: [u64; 8], pub difficulty: Difficulty, pub weights: Vec, pub values: Vec, @@ -62,23 +63,23 @@ pub const KERNEL: Option = None; impl crate::ChallengeTrait for Challenge { #[cfg(feature = "cuda")] fn cuda_generate_instance( - seed: u64, + seeds: [u64; 8], difficulty: &Difficulty, dev: &Arc, mut funcs: HashMap<&'static str, CudaFunction>, ) -> Result { // TIG dev bounty available for a GPU optimisation for instance generation! - Self::generate_instance(seed, difficulty) + Self::generate_instance(seeds, difficulty) } - fn generate_instance(seed: u64, difficulty: &Difficulty) -> Result { - let mut rng: StdRng = StdRng::seed_from_u64(seed); + fn generate_instance(seeds: [u64; 8], difficulty: &Difficulty) -> Result { + let mut rngs = RngArray::new(seeds); let weights: Vec = (0..difficulty.num_items) - .map(|_| rng.gen_range(1..50)) + .map(|_| rngs.get_mut().gen_range(1..50)) .collect(); let values: Vec = (0..difficulty.num_items) - .map(|_| rng.gen_range(1..50)) + .map(|_| rngs.get_mut().gen_range(1..50)) .collect(); let max_weight: u32 = weights.iter().sum::() / 2; @@ -103,7 +104,7 @@ impl crate::ChallengeTrait for Challenge { .round() as u32; Ok(Challenge { - seed, + seeds, difficulty: difficulty.clone(), weights, values, diff --git a/tig-challenges/src/lib.rs b/tig-challenges/src/lib.rs index c2ac8b23..42b27e16 100644 --- a/tig-challenges/src/lib.rs +++ b/tig-challenges/src/lib.rs @@ -1,4 +1,5 @@ use anyhow::{anyhow, Result}; +use rand::{rngs::StdRng, Rng, SeedableRng}; use serde::de::DeserializeOwned; use serde::Serialize; @@ -18,56 +19,56 @@ where T: SolutionTrait, U: DifficultyTrait, { - fn generate_instance(seed: u64, difficulty: &U) -> Result; - fn generate_instance_from_str(seed: u64, difficulty: &str) -> Result { - Self::generate_instance(seed, &serde_json::from_str(difficulty)?) + fn generate_instance(seeds: [u64; 8], difficulty: &U) -> Result; + fn generate_instance_from_str(seeds: [u64; 8], difficulty: &str) -> Result { + Self::generate_instance(seeds, &serde_json::from_str(difficulty)?) } - fn generate_instance_from_vec(seed: u64, difficulty: &Vec) -> Result { + fn generate_instance_from_vec(seeds: [u64; 8], difficulty: &Vec) -> Result { match difficulty.as_slice().try_into() { - Ok(difficulty) => Self::generate_instance_from_arr(seed, &difficulty), + Ok(difficulty) => Self::generate_instance_from_arr(seeds, &difficulty), Err(_) => Err(anyhow!("Invalid difficulty length")), } } - fn generate_instance_from_arr(seed: u64, difficulty: &[i32; N]) -> Result { - Self::generate_instance(seed, &U::from_arr(difficulty)) + fn generate_instance_from_arr(seeds: [u64; 8], difficulty: &[i32; N]) -> Result { + Self::generate_instance(seeds, &U::from_arr(difficulty)) } #[cfg(feature = "cuda")] fn cuda_generate_instance( - seed: u64, + seeds: [u64; 8], difficulty: &U, dev: &Arc, funcs: HashMap<&'static str, CudaFunction>, ) -> Result; #[cfg(feature = "cuda")] fn cuda_generate_instance_from_str( - seed: u64, + seeds: [u64; 8], difficulty: &str, dev: &Arc, funcs: HashMap<&'static str, CudaFunction>, ) -> Result { - Self::cuda_generate_instance(seed, &serde_json::from_str(difficulty)?, dev, funcs) + Self::cuda_generate_instance(seeds, &serde_json::from_str(difficulty)?, dev, funcs) } #[cfg(feature = "cuda")] fn cuda_generate_instance_from_vec( - seed: u64, + seeds: [u64; 8], difficulty: &Vec, dev: &Arc, funcs: HashMap<&'static str, CudaFunction>, ) -> Result { match difficulty.as_slice().try_into() { - Ok(difficulty) => Self::cuda_generate_instance_from_arr(seed, &difficulty, dev, funcs), + Ok(difficulty) => Self::cuda_generate_instance_from_arr(seeds, &difficulty, dev, funcs), Err(_) => Err(anyhow!("Invalid difficulty length")), } } #[cfg(feature = "cuda")] fn cuda_generate_instance_from_arr( - seed: u64, + seeds: [u64; 8], difficulty: &[i32; N], dev: &Arc, funcs: HashMap<&'static str, CudaFunction>, ) -> Result { - Self::cuda_generate_instance(seed, &U::from_arr(difficulty), dev, funcs) + Self::cuda_generate_instance(seeds, &U::from_arr(difficulty), dev, funcs) } fn verify_solution(&self, solution: &T) -> Result<()>; @@ -92,3 +93,20 @@ pub struct CudaKernel { pub src: &'static str, pub funcs: &'static [&'static str], } + +pub struct RngArray { + rngs: [StdRng; 8], + index: u32, +} + +impl RngArray { + pub fn new(seeds: [u64; 8]) -> Self { + let rngs = seeds.map(StdRng::seed_from_u64); + RngArray { rngs, index: 0 } + } + + pub fn get_mut(&mut self) -> &mut StdRng { + self.index = (&mut self.rngs[self.index as usize]).gen_range(0..8); + &mut self.rngs[self.index as usize] + } +} diff --git a/tig-challenges/src/satisfiability.rs b/tig-challenges/src/satisfiability.rs index d153f52b..f9ccb21a 100644 --- a/tig-challenges/src/satisfiability.rs +++ b/tig-challenges/src/satisfiability.rs @@ -1,10 +1,6 @@ use anyhow::{anyhow, Result}; use ndarray::{Array2, Axis}; -use rand::{ - distributions::{Distribution, Uniform}, - rngs::StdRng, - SeedableRng, -}; +use rand::distributions::{Distribution, Uniform}; use serde::{ de::{self, SeqAccess, Visitor}, ser::SerializeSeq, @@ -14,6 +10,7 @@ use serde_json::{from_value, Map, Value}; #[cfg(feature = "cuda")] use crate::CudaKernel; +use crate::RngArray; #[cfg(feature = "cuda")] use cudarc::driver::*; #[cfg(feature = "cuda")] @@ -59,7 +56,7 @@ impl TryFrom> for Solution { #[derive(Serialize, Deserialize, Debug)] pub struct Challenge { - pub seed: u64, + pub seeds: [u64; 8], pub difficulty: Difficulty, pub clauses: Vec>, } @@ -71,17 +68,17 @@ pub const KERNEL: Option = None; impl crate::ChallengeTrait for Challenge { #[cfg(feature = "cuda")] fn cuda_generate_instance( - seed: u64, + seeds: [u64; 8], difficulty: &Difficulty, dev: &Arc, mut funcs: HashMap<&'static str, CudaFunction>, ) -> Result { // TIG dev bounty available for a GPU optimisation for instance generation! - Self::generate_instance(seed, difficulty) + Self::generate_instance(seeds, difficulty) } - fn generate_instance(seed: u64, difficulty: &Difficulty) -> Result { - let mut rng = StdRng::seed_from_u64(seed); + fn generate_instance(seeds: [u64; 8], difficulty: &Difficulty) -> Result { + let mut rngs = RngArray::new(seeds); let num_clauses = (difficulty.num_variables as f64 * difficulty.clauses_to_variables_percent as f64 / 100.0) @@ -92,11 +89,12 @@ impl crate::ChallengeTrait for Challenge { let neg_distr = Uniform::new(0, 2); // Generate the clauses array. - let clauses_array = Array2::from_shape_fn((num_clauses, 3), |_| var_distr.sample(&mut rng)); + let clauses_array = + Array2::from_shape_fn((num_clauses, 3), |_| var_distr.sample(rngs.get_mut())); // Generate the negations array. let negations = Array2::from_shape_fn((num_clauses, 3), |_| { - if neg_distr.sample(&mut rng) == 0 { + if neg_distr.sample(rngs.get_mut()) == 0 { -1 } else { 1 @@ -113,7 +111,7 @@ impl crate::ChallengeTrait for Challenge { .collect(); Ok(Self { - seed, + seeds, difficulty: difficulty.clone(), clauses, }) diff --git a/tig-challenges/src/vector_search.rs b/tig-challenges/src/vector_search.rs index 9036dade..859876f5 100644 --- a/tig-challenges/src/vector_search.rs +++ b/tig-challenges/src/vector_search.rs @@ -1,10 +1,6 @@ -use crate::{ChallengeTrait, DifficultyTrait, SolutionTrait}; +use crate::{ChallengeTrait, DifficultyTrait, RngArray, SolutionTrait}; use anyhow::{anyhow, Ok, Result}; -use rand::{ - distributions::{Distribution, Uniform}, - rngs::StdRng, - SeedableRng, -}; +use rand::distributions::{Distribution, Uniform}; use serde::{Deserialize, Serialize}; use serde_json::{from_value, Map, Value}; @@ -51,7 +47,7 @@ impl TryFrom> for Solution { #[derive(Serialize, Deserialize, Debug)] pub struct Challenge { - pub seed: u64, + pub seeds: [u64; 8], pub difficulty: Difficulty, pub vector_database: Vec>, pub query_vectors: Vec>, @@ -73,28 +69,28 @@ pub const KERNEL: Option = None; impl ChallengeTrait for Challenge { #[cfg(feature = "cuda")] fn cuda_generate_instance( - seed: u64, + seeds: [u64; 8], difficulty: &Difficulty, dev: &Arc, mut funcs: HashMap<&'static str, CudaFunction>, ) -> Result { // TIG dev bounty available for a GPU optimisation for instance generation! - Self::generate_instance(seed, difficulty) + Self::generate_instance(seeds, difficulty) } - fn generate_instance(seed: u64, difficulty: &Difficulty) -> Result { - let mut rng = StdRng::seed_from_u64(seed); + fn generate_instance(seeds: [u64; 8], difficulty: &Difficulty) -> Result { + let mut rngs = RngArray::new(seeds); let uniform = Uniform::from(0.0..1.0); let search_vectors = (0..100000) - .map(|_| (0..250).map(|_| uniform.sample(&mut rng)).collect()) + .map(|_| (0..250).map(|_| uniform.sample(rngs.get_mut())).collect()) .collect(); let query_vectors = (0..difficulty.num_queries) - .map(|_| (0..250).map(|_| uniform.sample(&mut rng)).collect()) + .map(|_| (0..250).map(|_| uniform.sample(rngs.get_mut())).collect()) .collect(); let max_distance = 6.0 - (difficulty.better_than_baseline as f32) / 1000.0; Ok(Self { - seed, + seeds, difficulty: difficulty.clone(), vector_database: search_vectors, query_vectors, diff --git a/tig-challenges/src/vehicle_routing.rs b/tig-challenges/src/vehicle_routing.rs index 927ecbad..c7bcef31 100644 --- a/tig-challenges/src/vehicle_routing.rs +++ b/tig-challenges/src/vehicle_routing.rs @@ -1,10 +1,11 @@ use anyhow::{anyhow, Result}; -use rand::{rngs::StdRng, Rng, SeedableRng}; +use rand::Rng; use serde::{Deserialize, Serialize}; use serde_json::{from_value, Map, Value}; #[cfg(feature = "cuda")] use crate::CudaKernel; +use crate::RngArray; #[cfg(feature = "cuda")] use cudarc::driver::*; #[cfg(feature = "cuda")] @@ -46,7 +47,7 @@ impl TryFrom> for Solution { #[derive(Serialize, Deserialize, Debug)] pub struct Challenge { - pub seed: u64, + pub seeds: [u64; 8], pub difficulty: Difficulty, pub demands: Vec, pub distance_matrix: Vec>, @@ -61,27 +62,34 @@ pub const KERNEL: Option = None; impl crate::ChallengeTrait for Challenge { #[cfg(feature = "cuda")] fn cuda_generate_instance( - seed: u64, + seeds: [u64; 8], difficulty: &Difficulty, dev: &Arc, mut funcs: HashMap<&'static str, CudaFunction>, ) -> Result { // TIG dev bounty available for a GPU optimisation for instance generation! - Self::generate_instance(seed, difficulty) + Self::generate_instance(seeds, difficulty) } - fn generate_instance(seed: u64, difficulty: &Difficulty) -> Result { - let mut rng: StdRng = StdRng::seed_from_u64(seed); + fn generate_instance(seeds: [u64; 8], difficulty: &Difficulty) -> Result { + let mut rngs = RngArray::new(seeds); let num_nodes = difficulty.num_nodes; let max_capacity = 100; let mut node_positions: Vec<(f64, f64)> = (0..num_nodes) - .map(|_| (rng.gen::() * 500.0, rng.gen::() * 500.0)) + .map(|_| { + ( + rngs.get_mut().gen::() * 500.0, + rngs.get_mut().gen::() * 500.0, + ) + }) .collect(); node_positions[0] = (250.0, 250.0); // Depot is node 0, and in the center - let mut demands: Vec = (0..num_nodes).map(|_| rng.gen_range(15..30)).collect(); + let mut demands: Vec = (0..num_nodes) + .map(|_| rngs.get_mut().gen_range(15..30)) + .collect(); demands[0] = 0; // Depot demand is 0 let distance_matrix: Vec> = node_positions @@ -112,7 +120,7 @@ impl crate::ChallengeTrait for Challenge { / 1000) as i32; Ok(Challenge { - seed, + seeds, difficulty: difficulty.clone(), demands, distance_matrix, diff --git a/tig-structs/src/core.rs b/tig-structs/src/core.rs index f9c727a5..90142373 100644 --- a/tig-structs/src/core.rs +++ b/tig-structs/src/core.rs @@ -2,7 +2,7 @@ use crate::{config::ProtocolConfig, serializable_struct_with_getters}; use serde::{Deserialize, Serialize}; use serde_json::{Map, Value}; use std::collections::{HashMap, HashSet}; -use tig_utils::{jsonify, u32_from_str, u64_from_str}; +use tig_utils::{jsonify, u32_from_str, u64s_from_str}; pub use tig_utils::{Frontier, Point, PreciseNumber, Transaction, U256}; serializable_struct_with_getters! { @@ -109,8 +109,12 @@ serializable_struct_with_getters! { } } impl BenchmarkSettings { - pub fn calc_seed(&self, nonce: u64) -> u64 { - u64_from_str(jsonify(&self).as_str()) ^ nonce + pub fn calc_seeds(&self, nonce: u64) -> [u64; 8] { + let mut seeds = u64s_from_str(jsonify(&self).as_str()); + for seed in seeds.iter_mut() { + *seed ^= nonce; + } + seeds } } serializable_struct_with_getters! { diff --git a/tig-utils/src/hash.rs b/tig-utils/src/hash.rs index 98f72768..0826d6fd 100644 --- a/tig-utils/src/hash.rs +++ b/tig-utils/src/hash.rs @@ -1,5 +1,5 @@ use md5; -use sha3::{Digest, Keccak256}; +use sha3::{Digest, Keccak512}; pub fn md5_from_str(input: &str) -> String { md5_from_bytes(input.as_bytes()) @@ -15,13 +15,17 @@ pub fn u32_from_str(input: &str) -> u32 { u32::from_le_bytes(bytes) } -pub fn u64_from_str(input: &str) -> u64 { - let mut hasher = Keccak256::new(); +pub fn u64s_from_str(input: &str) -> [u64; 8] { + let mut hasher = Keccak512::new(); hasher.update(input.as_bytes()); let result = hasher.finalize(); - u64::from_le_bytes( - result.as_slice()[0..8] + + let mut output = [0u64; 8]; + for i in 0..8 { + let bytes = result[i * 8..(i + 1) * 8] .try_into() - .expect("Should not ever panic.."), - ) + .expect("Should not ever panic.."); + output[i] = u64::from_le_bytes(bytes); + } + output } diff --git a/tig-worker/src/worker.rs b/tig-worker/src/worker.rs index 0a5dc6ae..db4805f2 100644 --- a/tig-worker/src/worker.rs +++ b/tig-worker/src/worker.rs @@ -12,29 +12,29 @@ pub fn compute_solution( max_memory: u64, max_fuel: u64, ) -> Result> { - let seed = settings.calc_seed(nonce); + let seeds = settings.calc_seeds(nonce); let serialized_challenge = match settings.challenge_id.as_str() { "c001" => { let challenge = - satisfiability::Challenge::generate_instance_from_vec(seed, &settings.difficulty) + satisfiability::Challenge::generate_instance_from_vec(seeds, &settings.difficulty) .unwrap(); bincode::serialize(&challenge).unwrap() } "c002" => { let challenge = - vehicle_routing::Challenge::generate_instance_from_vec(seed, &settings.difficulty) + vehicle_routing::Challenge::generate_instance_from_vec(seeds, &settings.difficulty) .unwrap(); bincode::serialize(&challenge).unwrap() } "c003" => { let challenge = - knapsack::Challenge::generate_instance_from_vec(seed, &settings.difficulty) + knapsack::Challenge::generate_instance_from_vec(seeds, &settings.difficulty) .unwrap(); bincode::serialize(&challenge).unwrap() } "c004" => { let challenge = - vector_search::Challenge::generate_instance_from_vec(seed, &settings.difficulty) + vector_search::Challenge::generate_instance_from_vec(seeds, &settings.difficulty) .unwrap(); bincode::serialize(&challenge).unwrap() } @@ -88,7 +88,7 @@ pub fn compute_solution( // Get runtime signature let runtime_signature_u64 = store.get_runtime_signature(); let runtime_signature = (runtime_signature_u64 as u32) ^ ((runtime_signature_u64 >> 32) as u32); - let fuel_consumed = store.get_fuel().unwrap(); + let fuel_consumed = max_fuel - store.get_fuel().unwrap(); // Read solution from memory let mut solution_len_bytes = [0u8; 4]; memory @@ -121,11 +121,11 @@ pub fn verify_solution( nonce: u64, solution: &Solution, ) -> Result<()> { - let seed = settings.calc_seed(nonce); + let seeds = settings.calc_seeds(nonce); match settings.challenge_id.as_str() { "c001" => { let challenge = - satisfiability::Challenge::generate_instance_from_vec(seed, &settings.difficulty) + satisfiability::Challenge::generate_instance_from_vec(seeds, &settings.difficulty) .expect("Failed to generate satisfiability instance"); match satisfiability::Solution::try_from(solution.clone()) { Ok(solution) => challenge.verify_solution(&solution), @@ -136,7 +136,7 @@ pub fn verify_solution( } "c002" => { let challenge = - vehicle_routing::Challenge::generate_instance_from_vec(seed, &settings.difficulty) + vehicle_routing::Challenge::generate_instance_from_vec(seeds, &settings.difficulty) .expect("Failed to generate vehicle_routing instance"); match vehicle_routing::Solution::try_from(solution.clone()) { Ok(solution) => challenge.verify_solution(&solution), @@ -147,7 +147,7 @@ pub fn verify_solution( } "c003" => { let challenge = - knapsack::Challenge::generate_instance_from_vec(seed, &settings.difficulty) + knapsack::Challenge::generate_instance_from_vec(seeds, &settings.difficulty) .expect("Failed to generate knapsack instance"); match knapsack::Solution::try_from(solution.clone()) { Ok(solution) => challenge.verify_solution(&solution), @@ -158,7 +158,7 @@ pub fn verify_solution( } "c004" => { let challenge = - vector_search::Challenge::generate_instance_from_vec(seed, &settings.difficulty) + vector_search::Challenge::generate_instance_from_vec(seeds, &settings.difficulty) .expect("Failed to generate vector_search instance"); match vector_search::Solution::try_from(solution.clone()) { Ok(solution) => challenge.verify_solution(&solution),