Add validation logic to ProcessState

This commit is contained in:
Sosthene 2024-10-04 09:25:58 +02:00
parent 319da6b1a3
commit 3ebf4f7899

View File

@ -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<String, Value>, // We may not always have all the keys
pub validation_token: Vec<Proof>, // This signs the encrypted pcd
pub validation_tokens: Vec<Proof>, // 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<bool, anyhow::Error> {
// Determine modified fields
let modified_fields: Vec<String> = if let Some(previous_state) = previous_state
{
let res: Vec<String> = 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<String, RoleDefinition> = 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::<RoleDefinition>(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