Roles is now part of ProcessState out of the pcd

This commit is contained in:
NicolasCantu 2025-02-07 22:44:14 +01:00 committed by Nicolas Cantu
parent ab1c16ff81
commit 46d405f046
2 changed files with 16 additions and 56 deletions

View File

@ -1,7 +1,7 @@
use anyhow::{Error, Result}; use anyhow::{Error, Result};
use rs_merkle::{algorithms::Sha256, MerkleTree}; use rs_merkle::{algorithms::Sha256, MerkleTree};
use serde::ser::SerializeStruct; use serde::ser::SerializeStruct;
use std::collections::{HashMap, HashSet}; use std::collections::HashSet;
use std::hash::{Hash as StdHash, Hasher}; use std::hash::{Hash as StdHash, Hasher};
use std::fmt; use std::fmt;
@ -336,34 +336,6 @@ pub trait Pcd<'a>: Serialize + Deserialize<'a> {
} }
} }
fn extract_roles(&self) -> Result<HashMap<String, RoleDefinition>> {
let obj = self.to_value_object()?;
let parse_roles_map = |m: &Map<String, Value>| {
let mut res: HashMap<String, RoleDefinition> = HashMap::new();
for (name, role_def) in m {
res.insert(name.clone(), serde_json::from_value(role_def.clone())?);
}
<Result<HashMap<String, RoleDefinition>, Error>>::Ok(res)
};
if let Some(roles) = obj.get("roles") {
match roles {
Value::Object(m) => {
parse_roles_map(m)
},
Value::String(s) => {
let m: Map<String, Value> = serde_json::from_str(&s)?;
parse_roles_map(&m)
}
_ => Err(Error::msg("\"roles\" is not an object"))
}
} else {
Err(Error::msg("No \"roles\" key in this pcd"))
}
}
fn is_hex_string(&self, length: Option<usize>) -> Result<()> { fn is_hex_string(&self, length: Option<usize>) -> Result<()> {
let value = serde_json::to_value(self)?; let value = serde_json::to_value(self)?;
@ -1003,7 +975,6 @@ mod tests {
let new_state_merkle_root = Value::Object(new_state_commitments).create_merkle_tree().unwrap().root().unwrap(); let new_state_merkle_root = Value::Object(new_state_commitments).create_merkle_tree().unwrap().root().unwrap();
let validation_hash = AnkValidationYesHash::from_merkle_root(new_state_merkle_root); let validation_hash = AnkValidationYesHash::from_merkle_root(new_state_merkle_root);
// let validation_hash = AnkValidationNoHash::from_commitment(new_state_hash);
let alice_spend_key: SecretKey = alice_wallet let alice_spend_key: SecretKey = alice_wallet
.get_client() .get_client()

View File

@ -1,5 +1,5 @@
use std::{ use std::{
collections::{HashMap, HashSet}, collections::{BTreeMap, HashMap, HashSet},
sync::{Mutex, MutexGuard, OnceLock}, sync::{Mutex, MutexGuard, OnceLock},
}; };
@ -28,10 +28,12 @@ pub struct ProcessState {
pub validation_tokens: Vec<Proof>, // Signature of the hash of the encrypted pcd tagged with some decision like "yes" or "no" pub validation_tokens: Vec<Proof>, // Signature of the hash of the encrypted pcd tagged with some decision like "yes" or "no"
#[tsify(type = "Record<string, string>")] #[tsify(type = "Record<string, string>")]
pub descriptions: Map<String, Value>, // long descriptions that can be used for the ihm pub descriptions: Map<String, Value>, // long descriptions that can be used for the ihm
#[tsify(type = "Record<string, RoleDefinition>")]
pub roles: BTreeMap<String, RoleDefinition>,
} }
impl ProcessState { impl ProcessState {
pub fn new(commited_in: OutPoint, clear_state: Map<String, Value>, descriptions: Map<String, Value>) -> anyhow::Result<Self> { pub fn new(commited_in: OutPoint, clear_state: Map<String, Value>, descriptions: Map<String, Value>, roles: BTreeMap<String, RoleDefinition>) -> anyhow::Result<Self> {
// Check all descriptions matches clear_state // Check all descriptions matches clear_state
for (key, _) in &descriptions { for (key, _) in &descriptions {
clear_state.get(key).ok_or(anyhow::Error::msg("Missing field in descriptions"))?; clear_state.get(key).ok_or(anyhow::Error::msg("Missing field in descriptions"))?;
@ -59,6 +61,7 @@ impl ProcessState {
keys, keys,
validation_tokens: vec![], validation_tokens: vec![],
descriptions, descriptions,
roles,
}; };
Ok(res) Ok(res)
@ -160,22 +163,11 @@ impl ProcessState {
return Err(anyhow::anyhow!("State is identical to the previous state")); return Err(anyhow::anyhow!("State is identical to the previous state"));
} }
let roles2rules = match self.encrypted_pcd.extract_roles() {
Ok(roles) => roles,
Err(_) => {
let mut fields2plains = Map::new();
let fields2commit = self.pcd_commitment.as_object().ok_or(anyhow::Error::msg("pcd_commitment is not an object"))?;
self.encrypted_pcd
.decrypt_all(self.commited_in, &fields2commit, &self.keys, &mut fields2plains)?;
Value::Object(fields2plains).extract_roles()?
}
};
// 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| {
// Collect applicable rules from all roles for the current field // Collect applicable rules from all roles for the current field
let applicable_roles: Vec<RoleDefinition> = roles2rules let applicable_roles: Vec<RoleDefinition> = self.roles
.iter() .iter()
.filter_map(|(_, role_def)| { .filter_map(|(_, role_def)| {
let mut filtered_role_def = role_def.clone(); let mut filtered_role_def = role_def.clone();
@ -222,19 +214,15 @@ impl ProcessState {
} }
pub fn get_fields_to_validate_for_member(&self, member: &Member) -> anyhow::Result<Vec<String>> { pub fn get_fields_to_validate_for_member(&self, member: &Member) -> anyhow::Result<Vec<String>> {
let decrypted = self.decrypt_pcd()?;
let roles = Value::Object(decrypted).extract_roles()?;
let mut res: HashSet<String> = HashSet::new(); let mut res: HashSet<String> = HashSet::new();
// Are we in that role? // Are we in that role?
for (_, role_def) in roles { for (_, role_def) in &self.roles {
if !role_def.members.contains(member) { if !role_def.members.contains(member) {
continue; continue;
} else { } else {
// what are the fields we can modify? // what are the fields we can modify?
for rule in role_def.validation_rules { for rule in &role_def.validation_rules {
if rule.allows_modification() { if rule.allows_modification() {
res.extend(rule.fields.iter().map(|f| f.clone())); res.extend(rule.fields.iter().map(|f| f.clone()));
} }
@ -656,7 +644,7 @@ mod tests {
let carol = Member::new(vec![carol_address]).unwrap(); let carol = Member::new(vec![carol_address]).unwrap();
let validation_rule1 = let validation_rule1 =
ValidationRule::new(1.0, vec!["field1".to_owned(), "roles".to_owned()], 0.5).unwrap(); ValidationRule::new(1.0, vec!["field1".to_owned()], 0.5).unwrap();
let validation_rule2 = ValidationRule::new(1.0, vec!["field2".to_owned()], 0.5).unwrap(); let validation_rule2 = ValidationRule::new(1.0, vec!["field2".to_owned()], 0.5).unwrap();
let role_def1 = RoleDefinition { let role_def1 = RoleDefinition {
@ -670,18 +658,19 @@ mod tests {
storages: vec![] storages: vec![]
}; };
let roles: BTreeMap<String, RoleDefinition> = BTreeMap::from([
("role1".to_owned(), role_def1),
("role2".to_owned(), role_def2)
]);
let clear_pcd = json!({ let clear_pcd = json!({
"field1": "value1", "field1": "value1",
"field2": "value2", "field2": "value2",
"roles": {
"role1": role_def1,
"role2": role_def2
}
}); });
let outpoint = OutPoint::null(); let outpoint = OutPoint::null();
ProcessState::new(outpoint, clear_pcd.as_object().unwrap().clone(), Map::new()).unwrap() ProcessState::new(outpoint, clear_pcd.as_object().unwrap().clone(), Map::new(), roles).unwrap()
} }
#[test] #[test]