diff --git a/swagger.yaml b/swagger.yaml index fba958a2..61f76ed1 100644 --- a/swagger.yaml +++ b/swagger.yaml @@ -61,7 +61,7 @@ paths: * Returns all confirmed precommits, benchmarks, proofs, and frauds for the player where the benchmark was started within `120` blocks of the latest block. - * Fields `benchmark.solution_nonces`, `benchmark.discarded_solution_nonces`, `proof.merkle_proofs`, and `fraud.allegation` will always be `null` + * Fields `benchmark.solution_nonces`, `benchmark.discarded_solution_nonces`, `benchmark.non_solution_nonces`, `proof.merkle_proofs`, and `fraud.allegation` will always be `null` * To retrieve that data, use /get-benchmark-data endpoint parameters: @@ -90,8 +90,8 @@ paths: summary: Get all data for a benchmark description: |- # Notes - - * Will include data for fields `benchmark.solution_nonces`, `benchmark.discarded_solution_nonces`, `proof.merkle_proofs`, and `fraud.allegation`. + + * Will include data for fields `benchmark.solution_nonces`, `benchmark.discarded_solution_nonces`, `benchmark.non_solution_nonces`, `proof.merkle_proofs`, and `fraud.allegation`. parameters: - name: benchmark_id in: query @@ -689,11 +689,19 @@ components: items: type: integer format: uint64 + nullable: true discarded_solution_nonces: type: array items: type: integer format: uint64 + nullable: true + non_solution_nonces: + type: array + items: + type: integer + format: uint64 + nullable: true BenchmarkDetails: type: object properties: @@ -1110,6 +1118,7 @@ components: $ref: '#/components/schemas/FraudState' allegation: type: string + nullable: true FraudState: type: object properties: @@ -1322,6 +1331,7 @@ components: type: array items: $ref: '#/components/schemas/MerkleProof' + nullable: true ProofDetails: type: object properties: diff --git a/tig-benchmarker/common/structs.py b/tig-benchmarker/common/structs.py index 17085439..b5484e46 100644 --- a/tig-benchmarker/common/structs.py +++ b/tig-benchmarker/common/structs.py @@ -81,6 +81,8 @@ class Benchmark(FromDict): id: str details: BenchmarkDetails state: BenchmarkState + non_solution_nonces: Optional[Set[int]] + discarded_solution_nonces: Optional[Set[int]] solution_nonces: Optional[Set[int]] @dataclass diff --git a/tig-benchmarker/master/master/submissions_manager.py b/tig-benchmarker/master/master/submissions_manager.py index ed90b6a3..7d4c7db7 100644 --- a/tig-benchmarker/master/master/submissions_manager.py +++ b/tig-benchmarker/master/master/submissions_manager.py @@ -5,7 +5,7 @@ import logging import os from common.structs import * from common.utils import * -from typing import Union, Set, List, Dict +from typing import Union, Set, List, Dict, Optional from master.sql import get_db_conn from master.client_manager import CONFIG @@ -20,8 +20,9 @@ class SubmitPrecommitRequest(FromDict): class SubmitBenchmarkRequest(FromDict): benchmark_id: str merkle_root: MerkleHash - discarded_solution_nonces: Set[int] - solution_nonces: Set[int] + non_solution_nonces: Optional[List[int]] + discarded_solution_nonces: Optional[List[int]] + solution_nonces: Optional[List[int]] @dataclass class SubmitProofRequest(FromDict): @@ -116,10 +117,11 @@ class SubmissionsManager: ORDER BY block_started LIMIT 1 ) - RETURNING benchmark_id + RETURNING benchmark_id, num_nonces ) - SELECT - B.benchmark_id, + SELECT + A.benchmark_id, + A.num_nonces, B.merkle_root, B.solution_nonces, B.discarded_solution_nonces @@ -132,13 +134,28 @@ class SubmissionsManager: if benchmark_to_submit: benchmark_id = benchmark_to_submit["benchmark_id"] + num_nonces = benchmark_to_submit["num_nonces"] merkle_root = benchmark_to_submit["merkle_root"] + non_solution_nonces = list( + set(range(num_nonces)) - + set(benchmark_to_submit["solution_nonces"]) - + set(benchmark_to_submit["discarded_solution_nonces"]) + ) solution_nonces = benchmark_to_submit["solution_nonces"] discarded_solution_nonces = benchmark_to_submit["discarded_solution_nonces"] + max_size = (num_nonces + 2) // 3 + if len(solution_nonces) > max_size: + solution_nonces = None + elif len(discarded_solution_nonces) > max_size: + discarded_solution_nonces = None + else: + non_solution_nonces = None + self._post_thread("benchmark", SubmitBenchmarkRequest( benchmark_id=benchmark_id, merkle_root=merkle_root, + non_solution_nonces=non_solution_nonces, solution_nonces=solution_nonces, discarded_solution_nonces=discarded_solution_nonces, )) diff --git a/tig-protocol/src/context.rs b/tig-protocol/src/context.rs index 41c95846..fe329348 100644 --- a/tig-protocol/src/context.rs +++ b/tig-protocol/src/context.rs @@ -19,8 +19,9 @@ pub trait Context { &self, benchmark_id: String, details: BenchmarkDetails, - solution_nonces: HashSet, - discarded_solution_nonces: HashSet, + non_solution_nonces: Option>, + solution_nonces: Option>, + discarded_solution_nonces: Option>, ) -> Result<()>; async fn get_binary_details(&self, code_id: &String) -> Option; async fn add_binary_to_mempool(&self, code_id: String, details: BinaryDetails) -> Result<()>; diff --git a/tig-protocol/src/contracts/benchmarks.rs b/tig-protocol/src/contracts/benchmarks.rs index 9a19c700..3956d119 100644 --- a/tig-protocol/src/contracts/benchmarks.rs +++ b/tig-protocol/src/contracts/benchmarks.rs @@ -1,5 +1,6 @@ use crate::context::*; use anyhow::{anyhow, Result}; +use core::num; use logging_timer::time; use rand::{rngs::StdRng, seq::IteratorRandom, Rng, SeedableRng}; use std::collections::HashSet; @@ -114,8 +115,9 @@ pub async fn submit_benchmark( player_id: String, benchmark_id: String, merkle_root: MerkleHash, - solution_nonces: HashSet, - discarded_solution_nonces: HashSet, + non_solution_nonces: Option>, + solution_nonces: Option>, + discarded_solution_nonces: Option>, seed: u64, ) -> Result<()> { // check benchmark is not duplicate @@ -136,83 +138,89 @@ pub async fn submit_benchmark( )); } - // check solution nonces is valid + // check at least 2 sets of nonces are provided let precommit_details = ctx.get_precommit_details(&benchmark_id).await.unwrap(); let num_nonces = precommit_details.num_nonces as u64; - if !solution_nonces.iter().all(|n| *n < num_nonces) { - return Err(anyhow!("Invalid solution nonces")); + let max_set_size = ((num_nonces + 2) / 3) as usize; + + let mut nonces_sets = vec![ + &solution_nonces, + &discarded_solution_nonces, + &non_solution_nonces, + ]; + nonces_sets.sort_by_key(|x| x.is_none()); + if nonces_sets[1].is_none() || nonces_sets[2].is_some() { + return Err(anyhow!("Exactly 2 sets of nonces must be provided")); + } + let set_a = nonces_sets[0].as_ref().unwrap(); + let set_b = nonces_sets[1].as_ref().unwrap(); + if !set_a.is_disjoint(set_b) { + return Err(anyhow!("Nonces sets must be disjoint.",)); + } + if set_a.len() > max_set_size || set_b.len() > max_set_size { + return Err(anyhow!("The 2 smaller sets of nonces must be submitted")); + } + if !set_a.iter().all(|n| *n < num_nonces) || !set_b.iter().all(|n| *n < num_nonces) { + return Err(anyhow!("Invalid nonces")); } // random sample nonces let config = ctx.get_config().await; let mut rng = StdRng::seed_from_u64(seed); - let benchmark_config = &config.challenges[&settings.challenge_id].benchmarks; + let benchmark_config = &config.challenges[&settings.challenge_id] + .benchmarks + .max_samples; let max_samples = benchmark_config.max_samples; - - // sample nonces from solutions - let mut sampled_solution_nonces = HashSet::new(); - if !solution_nonces.is_empty() { - for _ in 0..25 { - if sampled_solution_nonces.len() == max_samples { - break; - } - sampled_solution_nonces.insert(*solution_nonces.iter().choose(&mut rng).unwrap()); - } - } - - // sample nonces from discarded solutions - let mut sampled_discarded_solution_nonces = HashSet::new(); - if !discarded_solution_nonces.is_empty() { - for _ in 0..25 { - if sampled_discarded_solution_nonces.len() == max_samples { - break; - } - sampled_discarded_solution_nonces - .insert(*discarded_solution_nonces.iter().choose(&mut rng).unwrap()); - } - } - - // sample nonces from non-solutions - let mut sampled_non_solution_nonces = HashSet::new(); - let num_non_solution_nonces = - num_nonces - solution_nonces.len() as u64 - discarded_solution_nonces.len() as u64; - if num_non_solution_nonces > 0 { - if num_non_solution_nonces * 2 <= num_nonces { - let non_solution_nonces: HashSet = (0..num_nonces) - .filter(|n| !solution_nonces.contains(n) && !discarded_solution_nonces.contains(n)) - .collect(); - for _ in 0..25 { - if sampled_non_solution_nonces.len() == max_samples { - break; + let mut sampled_nonces = HashSet::new(); + for set_x in [ + &solution_nonces, + &discarded_solution_nonces, + &non_solution_nonces, + ] { + let break_size = sampled_nonces.len() + max_samples; + if let Some(set_x) = set_x { + if !set_x.is_empty() { + for _ in 0..25 { + if sampled_nonces.len() == break_size { + break; + } + sampled_nonces.insert(*set_x.iter().choose(&mut rng).unwrap()); } - sampled_non_solution_nonces - .insert(*non_solution_nonces.iter().choose(&mut rng).unwrap()); } } else { - // if there are more non-solutions than solutions, sample from all non-solutions + // this set is at least 1/3 of the total nonces for _ in 0..25 { - if sampled_non_solution_nonces.len() == max_samples { + if sampled_nonces.len() == break_size { break; } - sampled_non_solution_nonces.insert(rng.gen_range(0..num_nonces)); + let nonce = rng.gen_range(0..num_nonces); + if !set_a.contains(&nonce) && !set_b.contains(&nonce) { + sampled_nonces.insert(nonce); + } } } } - - let sampled_nonces: HashSet = sampled_solution_nonces - .into_iter() - .chain(sampled_non_solution_nonces.into_iter()) - .chain(sampled_discarded_solution_nonces.into_iter()) - .collect(); + let num_solutions = if let Some(solution_nonces) = &solution_nonces { + solution_nonces.len() + } else { + num_nonces as usize - set_a.len() - set_b.len() + } as u32; + let num_discarded_solutions = + if let Some(discarded_solution_nonces) = &discarded_solution_nonces { + discarded_solution_nonces.len() + } else { + num_nonces as usize - set_a.len() - set_b.len() + } as u32; ctx.add_benchmark_to_mempool( benchmark_id, BenchmarkDetails { - num_solutions: solution_nonces.len() as u32, - num_discarded_solutions: discarded_solution_nonces.len() as u32, + num_solutions, + num_discarded_solutions, merkle_root, sampled_nonces, }, + non_solution_nonces, solution_nonces, discarded_solution_nonces, )