diff --git a/tig-protocol/src/contracts/benchmarks.rs b/tig-protocol/src/contracts/benchmarks.rs index 08f211ba..bf86e4dc 100644 --- a/tig-protocol/src/contracts/benchmarks.rs +++ b/tig-protocol/src/contracts/benchmarks.rs @@ -40,15 +40,6 @@ pub async fn submit_precommit( return Err(anyhow!("Invalid challenge '{}'", settings.challenge_id)); } - // verify min nonces - let min_nonces = config.benchmarks.min_nonces[&settings.challenge_id]; - if num_nonces < min_nonces { - return Err(anyhow!( - "Invalid num_nonces. Must be at least {}", - min_nonces - )); - } - // verify algorithm is active if !ctx .get_algorithm_state(&settings.algorithm_id) diff --git a/tig-protocol/src/contracts/opow.rs b/tig-protocol/src/contracts/opow.rs index 4d8138d8..a799c61b 100644 --- a/tig-protocol/src/contracts/opow.rs +++ b/tig-protocol/src/contracts/opow.rs @@ -128,111 +128,129 @@ pub(crate) async fn update(cache: &mut AddBlockCache) { frontier_indexes.insert(point, frontier_index); } } - solutions.sort_by(|(a_settings, _, _), (b_settings, _, _)| { - let a_index = frontier_indexes[&a_settings.difficulty]; - let b_index = frontier_indexes[&b_settings.difficulty]; - a_index.cmp(&b_index) - }); + let mut solutions_by_frontier_idx = + HashMap::>::new(); + for &x in solutions.iter() { + solutions_by_frontier_idx + .entry(frontier_indexes[&x.0.difficulty]) + .or_default() + .push(x); + } - let mut max_qualifiers_by_player = max_qualifiers_by_player.clone(); - let mut curr_frontier_index = 0; let challenge_data = active_challenges_block_data.get_mut(challenge_id).unwrap(); let prev_solution_ratio = active_challenges_prev_block_data .get(challenge_id) .map(|x| x.average_solution_ratio) .unwrap_or_default(); let min_solution_ratio = prev_solution_ratio * config.opow.min_solution_ratio_factor; - for (settings, &num_solutions, &num_nonces) in solutions.iter() { - let BenchmarkSettings { - player_id, - algorithm_id, - challenge_id, - difficulty, - .. - } = settings; + let min_num_nonces = config.opow.min_num_nonces; + let mut player_algorithm_solutions = HashMap::>::new(); + let mut player_solutions = HashMap::::new(); + let mut player_nonces = HashMap::::new(); - if curr_frontier_index != frontier_indexes[difficulty] - && challenge_data.num_qualifiers > config.opow.total_qualifiers_threshold + for frontier_idx in 0..solutions_by_frontier_idx.len() { + for (settings, &num_solutions, &num_nonces) in + solutions_by_frontier_idx[&frontier_idx].iter() { + let BenchmarkSettings { + player_id, + algorithm_id, + challenge_id, + difficulty, + .. + } = settings; + + let difficulty_parameters = &config.challenges.difficulty_parameters[challenge_id]; + let min_difficulty = difficulty_parameters.min_difficulty(); + let max_difficulty = difficulty_parameters.max_difficulty(); + if (0..difficulty.len()) + .into_iter() + .any(|i| difficulty[i] < min_difficulty[i] || difficulty[i] > max_difficulty[i]) + { + continue; + } + *player_algorithm_solutions + .entry(player_id.clone()) + .or_default() + .entry(algorithm_id.clone()) + .or_default() += num_solutions; + *player_solutions.entry(player_id.clone()).or_default() += num_solutions; + *player_nonces.entry(player_id.clone()).or_default() += num_nonces; + + challenge_data + .qualifier_difficulties + .insert(difficulty.clone()); + } + + // check if we have enough qualifiers + let player_solution_ratio: HashMap = player_solutions + .keys() + .map(|player_id| { + ( + player_id.clone(), + player_solutions[player_id] as f64 / player_nonces[player_id] as f64, + ) + }) + .collect(); + let player_qualifiers: HashMap = player_solution_ratio + .keys() + .map(|player_id| { + ( + player_id.clone(), + if player_nonces[player_id] >= min_num_nonces + && player_solution_ratio[player_id] >= min_solution_ratio + { + max_qualifiers_by_player[player_id].min(player_solutions[player_id]) + } else { + 0 + }, + ) + }) + .collect(); + + let num_qualifiers = player_qualifiers.values().sum::(); + if num_qualifiers >= config.opow.total_qualifiers_threshold + || frontier_idx == solutions_by_frontier_idx.len() - 1 + { + let mut sum_weighted_solution_ratio = 0.0; + for player_id in player_qualifiers.keys() { + let opow_data = active_opow_block_data.get_mut(player_id).unwrap(); + opow_data + .num_qualifiers_by_challenge + .insert(challenge_id.clone(), player_qualifiers[player_id]); + opow_data + .solution_ratio_by_challenge + .insert(challenge_id.clone(), player_solution_ratio[player_id]); + + sum_weighted_solution_ratio += + player_solution_ratio[player_id] * player_qualifiers[player_id] as f64; + + if player_qualifiers[player_id] > 0 { + for algorithm_id in player_algorithm_solutions[player_id].keys() { + let algorithm_data = + active_algorithms_block_data.get_mut(algorithm_id).unwrap(); + + algorithm_data.num_qualifiers_by_player.insert( + player_id.clone(), + (player_qualifiers[player_id] as f64 + * player_algorithm_solutions[player_id][algorithm_id] as f64 + / player_solutions[player_id] as f64) + .ceil() as u32, + ); + } + } + } + challenge_data.num_qualifiers = num_qualifiers; + challenge_data.average_solution_ratio = if num_qualifiers == 0 { + 0.0 + } else { + sum_weighted_solution_ratio / num_qualifiers as f64 + }; break; } - let difficulty_parameters = &config.challenges.difficulty_parameters[challenge_id]; - let min_difficulty = difficulty_parameters.min_difficulty(); - let max_difficulty = difficulty_parameters.max_difficulty(); - if (0..difficulty.len()) - .into_iter() - .any(|i| difficulty[i] < min_difficulty[i] || difficulty[i] > max_difficulty[i]) - { - continue; - } - curr_frontier_index = frontier_indexes[difficulty]; - let player_data = active_opow_block_data.get_mut(player_id).unwrap(); - let algorithm_data = active_algorithms_block_data.get_mut(algorithm_id).unwrap(); - - let max_qualifiers = max_qualifiers_by_player.get(player_id).unwrap().clone(); - let solution_ratio = num_solutions as f64 / num_nonces as f64; - let num_qualifiers = if solution_ratio >= min_solution_ratio { - num_solutions.min(max_qualifiers) - } else { - 0 - }; - max_qualifiers_by_player.insert(player_id.clone(), max_qualifiers - num_qualifiers); - - if num_qualifiers > 0 { - *player_data - .num_qualifiers_by_challenge - .entry(challenge_id.clone()) - .or_default() += num_qualifiers; - *algorithm_data - .num_qualifiers_by_player - .entry(player_id.clone()) - .or_default() += num_qualifiers; - challenge_data.num_qualifiers += num_qualifiers; - } - challenge_data - .qualifier_difficulties - .insert(difficulty.clone()); } } - // update reliability - for challenge_id in active_challenge_ids.iter() { - if !solutions_by_challenge.contains_key(challenge_id) { - continue; - } - let solutions = solutions_by_challenge.get(challenge_id).unwrap(); - let challenge_data = active_challenges_block_data.get_mut(challenge_id).unwrap(); - - let mut total_solutions = HashMap::::new(); - let mut total_nonces = HashMap::::new(); - for (settings, num_solutions, num_nonces) in solutions.iter() { - if !challenge_data - .qualifier_difficulties - .contains(&settings.difficulty) - { - continue; - } - *total_solutions - .entry(settings.player_id.clone()) - .or_default() += *num_solutions; - *total_nonces.entry(settings.player_id.clone()).or_default() += *num_nonces; - } - - let mut sum_weighted_solution_ratio = 0.0; - for player_id in total_solutions.keys() { - let solution_ratio = total_solutions[player_id] as f64 / total_nonces[player_id] as f64; - let opow_data = active_opow_block_data.get_mut(player_id).unwrap(); - if let Some(&num_qualifiers) = opow_data.num_qualifiers_by_challenge.get(challenge_id) { - sum_weighted_solution_ratio += solution_ratio * num_qualifiers as f64; - } - opow_data - .solution_ratio_by_challenge - .insert(challenge_id.clone(), solution_ratio); - } - challenge_data.average_solution_ratio = - sum_weighted_solution_ratio / challenge_data.num_qualifiers as f64; - } - // update frontiers for challenge_id in active_challenge_ids.iter() { let challenge_data = active_challenges_block_data.get_mut(challenge_id).unwrap(); diff --git a/tig-structs/src/config.rs b/tig-structs/src/config.rs index 58e67a3d..3bc9314f 100644 --- a/tig-structs/src/config.rs +++ b/tig-structs/src/config.rs @@ -68,7 +68,6 @@ serializable_struct_with_getters! { min_per_nonce_fee: PreciseNumber, min_base_fee: PreciseNumber, runtime_configs: HashMap, - min_nonces: HashMap, } } serializable_struct_with_getters! { @@ -114,6 +113,7 @@ serializable_struct_with_getters! { max_coinbase_outputs: usize, coinbase_update_period: u32, min_solution_ratio_factor: f64, + min_num_nonces: u32, } } serializable_struct_with_getters! {