Update process states validation logic

This commit is contained in:
Sosthene 2024-10-07 11:16:35 +02:00
parent 7a4344608e
commit 4744740303

View File

@ -24,49 +24,65 @@ pub struct ProcessState {
} }
impl ProcessState { impl ProcessState {
/// A state is valid if the attached validation_tokens satisfy the updated conditions defined in the encrypted_pcd fn compute_modified_fields(&self, previous_state: Option<&ProcessState>) -> Vec<String> {
pub fn is_valid(&self, previous_state: Option<&ProcessState>) -> Result<bool, anyhow::Error> { let new_state = &self.encrypted_pcd;
// Determine modified fields
let modified_fields: Vec<String> = if let Some(previous_state) = previous_state { // Ensure the new state is a JSON object
let res: Vec<String> = self let new_state_obj = new_state
.as_object()
.expect("New state should be a JSON object");
if let Some(prev_state) = previous_state {
// Previous state exists; compute differences
let previous_state_obj = prev_state
.encrypted_pcd .encrypted_pcd
.as_object() .as_object()
.unwrap() .expect("Previous state should be a JSON object");
// Compute modified fields by comparing with previous state
new_state_obj
.iter() .iter()
.filter_map(|(key, value)| { .filter_map(|(key, value)| {
let previous_value = previous_state.encrypted_pcd.get(key); let previous_value = previous_state_obj.get(key);
if previous_value.is_none() || value != previous_value.unwrap() { if previous_value.is_none() || value != previous_value.unwrap() {
Some(key.clone()) Some(key.clone())
} else { } else {
None 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() .collect()
}; } else {
// No previous state; all fields are considered modified
new_state_obj.keys().cloned().collect()
}
}
pub fn is_valid(
&self,
previous_state: Option<&ProcessState>,
) -> anyhow::Result<()> {
if self.validation_tokens.is_empty() {
return Err(anyhow::anyhow!("Can't validate a state with no proofs attached"));
}
// Compute modified fields
let modified_fields = self.compute_modified_fields(previous_state);
if modified_fields.is_empty() {
return Err(anyhow::anyhow!("State is identical to the previous state"));
}
println!("modified fields: {:?}", modified_fields);
// Compute the hash of the new state
let new_state_hash = AnkPcdHash::from_value(&self.encrypted_pcd);
// Extract roles and their definitions
let mut fields2plains = Map::new(); let mut fields2plains = Map::new();
self.encrypted_pcd self.encrypted_pcd
.decrypt_fields(&self.keys, &mut fields2plains)?; .decrypt_fields(&self.keys, &mut fields2plains)?;
let mut roles2rules: HashMap<String, RoleDefinition> = HashMap::new(); let mut roles2rules: HashMap<String, RoleDefinition> = HashMap::new();
if let Some(roles) = fields2plains.get("roles") { 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() { if let Some(roles_map) = roles.as_object() {
for (role, conditions) in roles_map { for (role, conditions) in roles_map {
let role_def = serde_json::from_value::<RoleDefinition>(conditions.clone())?; let role_def = serde_json::from_value::<RoleDefinition>(conditions.clone())?;
@ -81,31 +97,43 @@ impl ProcessState {
// Check if each modified field satisfies at least one applicable rule across all roles // Check if each modified field satisfies at least one applicable rule across all roles
let all_fields_validated = modified_fields.iter().all(|field| { let all_fields_validated = modified_fields.iter().all(|field| {
let applicable_rules: Vec<(&RoleDefinition, &ValidationRule)> = roles2rules println!("validating field: {}", field);
.values() // Collect applicable rules from all roles for the current field
.flat_map(|role_def| { let applicable_roles: Vec<RoleDefinition> = roles2rules.iter()
role_def .map(|(_, role_def)| {
.get_applicable_rules(field) let mut filtered_role_def = role_def.clone();
.into_iter() let rules = filtered_role_def.get_applicable_rules(field);
.map(move |rule| (role_def, rule)) filtered_role_def.validation_rules = rules.into_iter().map(|r| r.clone()).collect();
}) filtered_role_def
})
.collect(); .collect();
if applicable_rules.is_empty() { if applicable_roles.is_empty() {
return false; // No rules apply to this field, consider it invalid return false; // No rules apply to this field, consider it invalid
} }
applicable_rules.into_iter().any(|(role_def, rule)| { println!("applicable_roles: {:?}", applicable_roles);
rule.is_satisfied(
field, // Check if any applicable rule is satisfied
AnkPcdHash::from_value(&self.encrypted_pcd), applicable_roles.into_iter().any(|role_def| {
&self.validation_tokens, let res = false;
&role_def.members, for rule in role_def.validation_rules {
) rule.is_satisfied(
field,
new_state_hash.clone(),
&self.validation_tokens,
&role_def.members,
);
}
res
}) })
}); });
Ok(all_fields_validated) if all_fields_validated {
Ok(())
} else {
Err(anyhow::anyhow!("Not enough valid proofs"))
}
} }
} }