Compare commits

..

2 Commits

Author SHA1 Message Date
Sosthene
dab1a4dd2c More verbose error messages for is_valid() 2025-09-02 12:51:42 +02:00
Sosthene
f65dd126ff Impl insert() in Roles 2025-09-02 12:51:20 +02:00
2 changed files with 82 additions and 67 deletions

View File

@ -654,6 +654,10 @@ impl Roles {
pub fn iter_mut(&mut self) -> std::collections::btree_map::IterMut<'_, String, RoleDefinition> { pub fn iter_mut(&mut self) -> std::collections::btree_map::IterMut<'_, String, RoleDefinition> {
self.0.iter_mut() self.0.iter_mut()
} }
pub fn insert(&mut self, key: String, value: RoleDefinition) -> Option<RoleDefinition> {
self.0.insert(key, value)
}
} }
#[cfg(test)] #[cfg(test)]

View File

@ -1,6 +1,5 @@
use anyhow::Result; use anyhow::Result;
use std::collections::{BTreeMap, HashMap, HashSet}; use std::collections::{BTreeMap, HashMap, HashSet};
use std::str::FromStr;
use std::sync::{Mutex, MutexGuard, OnceLock}; use std::sync::{Mutex, MutexGuard, OnceLock};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -14,7 +13,7 @@ use crate::{
pcd::{Member, Pcd, PcdCommitments, RoleDefinition, Roles, ValidationRule}, pcd::{Member, Pcd, PcdCommitments, RoleDefinition, Roles, ValidationRule},
serialization::{deserialize_hex, hex_array_btree, serialize_hex, OutPointMemberMap}, serialization::{deserialize_hex, hex_array_btree, serialize_hex, OutPointMemberMap},
signature::{AnkHash, AnkValidationNoHash, AnkValidationYesHash, Proof}, signature::{AnkHash, AnkValidationNoHash, AnkValidationYesHash, Proof},
MutexExt, SpecialRoles, APOPHIS, PAIREDADDRESSES, PAIRING, MutexExt, APOPHIS, PAIREDADDRESSES, PAIRING,
}; };
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, Tsify)] #[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, Tsify)]
@ -236,55 +235,61 @@ impl ProcessState {
// If we don't update pairedAddresses, we don't need to bother about pairing // If we don't update pairedAddresses, we don't need to bother about pairing
} }
// Check if each modified field satisfies at least one applicable rule across all roles if self.pcd_commitment.is_empty() {
let all_fields_validated = !self.pcd_commitment.is_empty() return Err(anyhow::anyhow!("No fields to validate"));
&& self.pcd_commitment.keys().all(|field| { }
// Collect applicable rules from all roles for the current field
let applicable_roles: Vec<RoleDefinition> = self let mut roles = self.roles.clone();
.roles
.iter() // If we're initial state, we add the demiurge role, otherwise we can just ignore it
.filter_map(|(role_name, role_def)| { if previous_state.is_none() {
if let Ok(special_role) = SpecialRoles::from_str(&role_name) { if let Some(demiurge) = roles.get(crate::DEMIURGE) {
match special_role { match self.handle_demiurge(demiurge) {
// We allow for a special case with a role that works only for initial state Ok(role) => {
// That's optional though log::debug!("Adding demiurge role: {:?}", role);
SpecialRoles::Demiurge => { roles.insert(crate::DEMIURGE.to_string(), role);
// If we're not in initial state just ignore it }
if previous_state.is_some() {
return None;
} else {
// We try to validate with demiurge
match self.handle_demiurge(role_def) {
Ok(role) => return Some(role),
Err(e) => { Err(e) => {
log::error!("{}", e.to_string()); log::debug!("{}", e.to_string());
return None;
} }
} }
} else {
log::debug!("No demiurge role for initial state");
} }
// Otherwise we just continue normal validation } else {
log::debug!("Not initial state, ignoring demiurge role");
} }
// We already handled other special roles
_ => return None, let mut error_msg = String::new();
}
} log::debug!("Checking fields for roles: {:?}", roles);
let mut filtered_role_def = role_def.clone();
let rules = filtered_role_def.get_applicable_rules(field); // Check if each modified field satisfies at least one applicable rule across all roles
filtered_role_def.validation_rules = let all_fields_validated: bool = self.pcd_commitment.keys().all(|field| {
// Collect applicable rules from all roles for the current field
let applicable_roles: Vec<(String, RoleDefinition)> = roles.clone()
.into_iter()
.filter_map(|(role_name, mut role_def)| {
let rules = role_def.get_applicable_rules(field);
role_def.validation_rules =
rules.into_iter().map(|r| r.clone()).collect(); rules.into_iter().map(|r| r.clone()).collect();
if filtered_role_def.validation_rules.is_empty() { if role_def.validation_rules.is_empty() {
None None
} else { } else {
Some(filtered_role_def) Some((role_name.clone(), role_def))
} }
}) })
.collect(); .collect();
if applicable_roles.is_empty() { if applicable_roles.is_empty() {
return false; // No rules apply to this field, consider it invalid error_msg = format!("No relevant roles for field: {}", field);
return false;
} }
applicable_roles.into_iter().any(|role_def| { let mut tested_roles = vec![];
if let Some((role_name, _valid_role)) = applicable_roles.into_iter().find(|(role_name, role_def)| {
tested_roles.push(role_name.clone());
role_def.validation_rules.iter().any(|rule| { role_def.validation_rules.iter().any(|rule| {
let members: Vec<&Member> = role_def let members: Vec<&Member> = role_def
.members .members
@ -299,13 +304,19 @@ impl ProcessState {
) )
.is_ok() .is_ok()
}) })
}) }) {
log::debug!("Validated field {} with role: {}", field, role_name);
return true;
} else {
error_msg = format!("Failed validation for field {} in roles: {:?}", field, tested_roles);
return false;
}
}); });
if all_fields_validated { if all_fields_validated {
Ok(()) Ok(())
} else { } else {
Err(anyhow::anyhow!("Not enough valid proofs")) Err(anyhow::anyhow!("Not enough valid proofs: {}", error_msg))
} }
} }
@ -960,7 +971,7 @@ mod tests {
.push(Proof::new(message_hash, signing_key)); .push(Proof::new(message_hash, signing_key));
let result = state.is_valid(None, &OutPointMemberMap(get_members_map())); let result = state.is_valid(None, &OutPointMemberMap(get_members_map()));
assert!(result.is_err()); assert!(result.is_err());
assert_eq!(result.unwrap_err().to_string(), "Not enough valid proofs"); assert_eq!(result.unwrap_err().to_string(), "Not enough valid proofs: Failed validation for field field1 in roles: [\"demiurge\", \"role1\"]");
} }
#[test] #[test]
@ -975,7 +986,7 @@ mod tests {
.push(Proof::new(message_hash, carol_key)); .push(Proof::new(message_hash, carol_key));
let result = state.is_valid(None, &OutPointMemberMap(get_members_map())); let result = state.is_valid(None, &OutPointMemberMap(get_members_map()));
assert!(result.is_err()); assert!(result.is_err());
assert_eq!(result.unwrap_err().to_string(), "Not enough valid proofs"); assert_eq!(result.unwrap_err().to_string(), "Not enough valid proofs: Failed validation for field field1 in roles: [\"demiurge\", \"role1\"]");
} }
#[test] #[test]
@ -1325,7 +1336,7 @@ mod tests {
&OutPointMemberMap(get_members_map()), &OutPointMemberMap(get_members_map()),
); );
assert!(result.is_err()); assert!(result.is_err());
assert_eq!(result.unwrap_err().to_string(), "Not enough valid proofs"); assert_eq!(result.unwrap_err().to_string(), "Not enough valid proofs: Failed validation for field field2 in roles: [\"role2\"]");
} }
#[test] #[test]