From 3ebf4f78993392cbbdd0fe2c04db175e4df23e2d Mon Sep 17 00:00:00 2001 From: Sosthene Date: Fri, 4 Oct 2024 09:25:58 +0200 Subject: [PATCH] Add validation logic to ProcessState --- src/process.rs | 78 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 77 insertions(+), 1 deletion(-) diff --git a/src/process.rs b/src/process.rs index f837c37..653c116 100644 --- a/src/process.rs +++ b/src/process.rs @@ -11,7 +11,83 @@ pub struct ProcessState { pub commited_in: OutPoint, pub encrypted_pcd: Value, // Some fields may be clear, if the owner of the process decides so pub keys: Map, // We may not always have all the keys - pub validation_token: Vec, // This signs the encrypted pcd + pub validation_tokens: Vec, // Signature of the hash of the encrypted pcd tagged with some decision like "yes" or "no" +} + +impl ProcessState { + /// A state is valid if the attached validation_tokens satisfy the updated conditions defined in the encrypted_pcd + pub fn is_valid(&self, previous_state: Option<&ProcessState>) -> Result { + // Determine modified fields + let modified_fields: Vec = if let Some(previous_state) = previous_state + { + let res: Vec = self.encrypted_pcd.as_object().unwrap() + .iter() + .filter_map(|(key, value)| { + let previous_value = previous_state.encrypted_pcd.get(key); + if previous_value.is_none() || value != previous_value.unwrap() { + Some(key.clone()) + } else { + None + } + }) + .collect(); + + if res.is_empty() { + return Err(anyhow::anyhow!("State is identical to the previous state")); + } + + res + } else { + self.encrypted_pcd.as_object().unwrap() + .keys() + .cloned() + .collect() + }; + + // Extract roles and their definitions + let mut fields2plains = Map::new(); + self.encrypted_pcd.decrypt_fields(&self.keys, &mut fields2plains)?; + + let mut roles2rules: HashMap = HashMap::new(); + if let Some(roles) = fields2plains.get("roles") { + if roles.is_null() { + return Err(anyhow::anyhow!("Can't decrypt roles in the encrypted pcd")); + } + if let Some(roles_map) = roles.as_object() { + for (role, conditions) in roles_map { + let role_def = serde_json::from_value::(conditions.clone())?; + roles2rules.insert(role.to_string(), role_def); + } + } else { + return Err(anyhow::anyhow!("Roles is not an object")); + } + } else { + return Err(anyhow::anyhow!("Missing roles in the encrypted pcd")); + } + + // Check if each modified field satisfies at least one applicable rule across all roles + let all_fields_validated = modified_fields.iter().all(|field| { + let applicable_rules: Vec<(&RoleDefinition, &ValidationRule)> = roles2rules.values() + .flat_map(|role_def| { + role_def.get_applicable_rules(field) + .into_iter() + .map(move |rule| (role_def, rule)) + }) + .collect(); + + if applicable_rules.is_empty() { + return false; // No rules apply to this field, consider it invalid + } + + let validation_tokens: Vec<&Proof> = self.validation_tokens.iter().collect(); + + applicable_rules.into_iter().any(|(role_def, rule)| { + rule.is_satisfied(field, AnkPcdHash::from_value(&self.encrypted_pcd), &validation_tokens, &role_def.members) + }) + }); + + Ok(all_fields_validated) + } } /// A process is basically a succession of states