Skip to main content

openrank_common/runners/
verification_runner.rs

1use crate::{
2    algos::et::convergence_check,
3    merkle::{self, fixed::DenseMerkleTree, hash_leaf, Hash},
4    tx::trust::{ScoreEntry, TrustEntry},
5    Domain, DomainHash,
6};
7use getset::Getters;
8use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
9use sha3::Keccak256;
10use std::collections::{BTreeMap, HashMap};
11use tracing::info;
12
13use super::{BaseRunner, Error as BaseError};
14
15#[derive(Getters)]
16#[getset(get = "pub")]
17/// Struct containing the state of the verification runner
18pub struct VerificationRunner {
19    base: BaseRunner,
20    compute_scores: HashMap<DomainHash, HashMap<Hash, Vec<ScoreEntry>>>,
21    compute_tree: HashMap<DomainHash, HashMap<Hash, DenseMerkleTree<Keccak256>>>,
22    commitments: HashMap<Hash, Hash>,
23}
24
25impl VerificationRunner {
26    pub fn new(domains: &[Domain]) -> Self {
27        let base = BaseRunner::new(domains);
28        let mut compute_scores = HashMap::new();
29        let mut compute_tree = HashMap::new();
30        for domain in domains {
31            let domain_hash = domain.to_hash();
32            compute_scores.insert(domain_hash, HashMap::new());
33            compute_tree.insert(domain_hash, HashMap::new());
34        }
35        Self {
36            base,
37            compute_scores,
38            compute_tree,
39            commitments: HashMap::new(),
40        }
41    }
42
43    /// Update the state of trees for certain domain, with the given trust entries
44    pub fn update_trust(
45        &mut self,
46        domain: Domain,
47        trust_entries: Vec<TrustEntry>,
48    ) -> Result<(), Error> {
49        self.base
50            .update_trust(domain, trust_entries)
51            .map_err(Error::Base)
52    }
53
54    pub fn update_trust_map(
55        &mut self,
56        domain: Domain,
57        trust_entries: Vec<TrustEntry>,
58    ) -> Result<(), Error> {
59        self.base
60            .update_trust_map(domain, trust_entries)
61            .map_err(Error::Base)
62    }
63
64    /// Update the state of trees for certain domain, with the given seed entries
65    pub fn update_seed(
66        &mut self,
67        domain: Domain,
68        seed_entries: Vec<ScoreEntry>,
69    ) -> Result<(), Error> {
70        self.base
71            .update_seed(domain, seed_entries)
72            .map_err(Error::Base)
73    }
74
75    pub fn update_seed_map(
76        &mut self,
77        domain: Domain,
78        seed_entries: Vec<ScoreEntry>,
79    ) -> Result<(), Error> {
80        self.base
81            .update_seed_map(domain, seed_entries)
82            .map_err(Error::Base)
83    }
84
85    /// Add a new commitment of certain assignment
86    pub fn update_commitment(&mut self, compute_id: Hash, commitment: Hash) {
87        self.commitments.insert(compute_id, commitment);
88    }
89
90    /// Add a new scores of certain transaction, for certain domain
91    pub fn update_scores(
92        &mut self,
93        domain: Domain,
94        compute_id: Hash,
95        compute_scores: Vec<ScoreEntry>,
96    ) -> Result<(), Error> {
97        let score_values = self
98            .compute_scores
99            .get_mut(&domain.clone().to_hash())
100            .ok_or(Error::ComputeScoresNotFoundWithDomain(domain.to_hash()))?;
101        score_values.insert(compute_id, compute_scores);
102        Ok(())
103    }
104
105    /// Get the list of completed assignments for certain domain
106    pub fn verify_job(&mut self, domain: Domain, compute_id: Hash) -> Result<bool, Error> {
107        info!("COMPLETED_ASSIGNMENT_SEARCH: {}", domain.to_hash());
108        let commitment = self.commitments.get(&compute_id.clone()).unwrap();
109        let cp_root = commitment.clone();
110
111        self.create_compute_tree(domain.clone(), compute_id.clone())?;
112        let (res_lt_root, res_compute_root) =
113            self.get_root_hashes(domain.clone(), compute_id.clone())?;
114        info!(
115            "LT_ROOT: {}, COMPUTE_ROOT: {}",
116            res_lt_root, res_compute_root
117        );
118        let is_root_equal = cp_root == res_compute_root;
119        let is_converged = self.compute_verification(domain.clone(), compute_id.clone())?;
120        info!(
121            "COMPLETED_ASSIGNMENT, DOMAIN: {}, is_root_equal: {}, is_converged: {}",
122            domain.to_hash(),
123            is_root_equal,
124            is_converged,
125        );
126
127        return Ok(is_root_equal && is_converged);
128    }
129
130    /// Get the list of completed assignments for certain domain
131    pub fn verify_scores(&mut self, domain: Domain, compute_id: Hash) -> Result<bool, Error> {
132        info!("COMPLETED_ASSIGNMENT_SEARCH: {}", domain.to_hash());
133
134        self.create_compute_tree(domain.clone(), compute_id.clone())?;
135        let (res_lt_root, res_compute_root) =
136            self.get_root_hashes(domain.clone(), compute_id.clone())?;
137        info!(
138            "LT_ROOT: {}, COMPUTE_ROOT: {}",
139            res_lt_root, res_compute_root
140        );
141        let is_converged = self.compute_verification(domain.clone(), compute_id.clone())?;
142        info!(
143            "COMPLETED_ASSIGNMENT, DOMAIN: {}, is_converged: {}",
144            domain.to_hash(),
145            is_converged,
146        );
147
148        return Ok(is_converged);
149    }
150
151    /// Build the compute tree of certain assignment, for certain domain.
152    fn create_compute_tree(&mut self, domain: Domain, compute_id: Hash) -> Result<(), Error> {
153        info!("CREATE_COMPUTE_TREE: {}", domain.to_hash());
154        let compute_tree_map = self
155            .compute_tree
156            .get_mut(&domain.to_hash())
157            .ok_or(Error::ComputeTreeNotFoundWithDomain(domain.to_hash()))?;
158        let compute_scores = self
159            .compute_scores
160            .get(&domain.to_hash())
161            .ok_or(Error::ComputeScoresNotFoundWithDomain(domain.to_hash()))?;
162        let scores = compute_scores.get(&compute_id).unwrap();
163        let score_entries: Vec<f32> = scores.iter().map(|x| *x.value()).collect();
164        let score_hashes: Vec<Hash> = score_entries
165            .par_iter()
166            .map(|&x| hash_leaf::<Keccak256>(x.to_be_bytes().to_vec()))
167            .collect();
168        let compute_tree =
169            DenseMerkleTree::<Keccak256>::new(score_hashes).map_err(Error::Merkle)?;
170        info!(
171            "COMPUTE_TREE_ROOT_HASH: {}",
172            compute_tree.root().map_err(Error::Merkle)?
173        );
174        compute_tree_map.insert(compute_id, compute_tree);
175
176        Ok(())
177    }
178
179    /// Get the verification result(True or False) of certain assignment, for certain domain
180    fn compute_verification(&mut self, domain: Domain, compute_id: Hash) -> Result<bool, Error> {
181        let compute_scores = self
182            .compute_scores
183            .get(&domain.to_hash())
184            .ok_or(Error::ComputeScoresNotFoundWithDomain(domain.to_hash()))?;
185        let domain_indices = self
186            .base
187            .indices
188            .get(&domain.to_hash())
189            .ok_or::<Error>(BaseError::IndicesNotFound(domain.to_hash()).into())?;
190        let lt = self
191            .base
192            .local_trust
193            .get(&domain.trust_namespace())
194            .ok_or::<Error>(BaseError::LocalTrustNotFound(domain.trust_namespace()).into())?;
195        let count = self
196            .base
197            .count
198            .get(&domain.to_hash())
199            .ok_or::<Error>(BaseError::CountNotFound(domain.to_hash()).into())?;
200        let seed = self
201            .base
202            .seed_trust
203            .get(&domain.seed_namespace())
204            .ok_or::<Error>(BaseError::SeedTrustNotFound(domain.seed_namespace()).into())?;
205        let scores = compute_scores.get(&compute_id).unwrap();
206        let score_entries: BTreeMap<u64, f32> = {
207            let mut score_entries_map: BTreeMap<u64, f32> = BTreeMap::new();
208            for entry in scores {
209                let i = domain_indices
210                    .get(entry.id())
211                    .ok_or(Error::DomainIndexNotFound(entry.id().clone()))?;
212                score_entries_map.insert(*i, *entry.value());
213            }
214            score_entries_map
215        };
216        Ok(convergence_check(
217            lt.clone(),
218            seed.clone(),
219            &score_entries,
220            *count,
221        ))
222    }
223
224    /// Get the local trust tree root and compute tree root of certain assignment, for certain domain
225    pub fn get_root_hashes(
226        &self,
227        domain: Domain,
228        assignment_id: Hash,
229    ) -> Result<(Hash, Hash), Error> {
230        let tree_roots = self.base.get_base_root_hashes(&domain)?;
231
232        let compute_tree_map = self
233            .compute_tree
234            .get(&domain.to_hash())
235            .ok_or(Error::ComputeTreeNotFoundWithDomain(domain.to_hash()))?;
236        let compute_tree = compute_tree_map.get(&assignment_id).unwrap();
237        let ct_tree_root = compute_tree.root().map_err(Error::Merkle)?;
238
239        Ok((tree_roots, ct_tree_root))
240    }
241}
242
243#[derive(thiserror::Error, Debug)]
244pub enum Error {
245    #[error("{0}")]
246    Base(BaseError),
247    #[error("compute_tree not found for domain: {0}")]
248    ComputeTreeNotFoundWithDomain(DomainHash),
249    #[error("compute_scores not found for domain: {0}")]
250    ComputeScoresNotFoundWithDomain(DomainHash),
251    #[error("active_assignments not found for domain: {0}")]
252    ActiveAssignmentsNotFound(DomainHash),
253    #[error("domain_indice not found for address: {0}")]
254    DomainIndexNotFound(String),
255    #[error("{0}")]
256    Merkle(merkle::Error),
257}
258
259impl From<BaseError> for Error {
260    fn from(err: BaseError) -> Self {
261        Self::Base(err)
262    }
263}