diff --git a/src/pcd.rs b/src/pcd.rs index 4555516..8ac5411 100644 --- a/src/pcd.rs +++ b/src/pcd.rs @@ -17,6 +17,7 @@ use sp_client::{ }; use tsify::Tsify; +use crate::serialization::OutPointMemberMap; use crate::{ signature::{AnkHash, AnkValidationNoHash, AnkValidationYesHash, Proof}, serialization::hex_array_btree @@ -316,7 +317,7 @@ impl ValidationRule { field: &str, merkle_root: [u8; 32], proofs: &[Proof], - members: &[Member], + members: &[&Member], ) -> Result<()> { // Check if this rule applies to the field if !self.fields.contains(&field.to_string()) { @@ -329,6 +330,7 @@ impl ValidationRule { let validating_members = members .iter() .filter(|member| { + if member.sp_addresses.is_empty() { return false }; // This can happen when a member in the rule wasn't found in the network let member_proofs: Vec<&Proof> = proofs .iter() .filter(|p| member.key_is_part_of_member(&p.get_key())) @@ -398,7 +400,7 @@ impl ValidationRule { #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Tsify)] #[tsify(into_wasm_abi, from_wasm_abi)] pub struct RoleDefinition { - pub members: Vec, + pub members: Vec, // We use the pairing process id so we don't have to update the role if the user add a device pub validation_rules: Vec, pub storages: Vec, } @@ -409,11 +411,24 @@ impl RoleDefinition { diff: Vec, new_state_merkle_root: [u8; 32], proofs: &[Proof], + members_list: &OutPointMemberMap, ) -> Result<()> { + let empty_member = Member::new(vec![]); if diff.iter().all(|field| { self.validation_rules .iter() - .any(|rule| rule.is_satisfied(field, new_state_merkle_root, proofs, &self.members).is_ok()) + .any(|rule| { + let members: Vec<&Member> = self.members.iter() + .map(|outpoint| { + if let Some(member) = members_list.0.get(outpoint) { + member + } else { + &empty_member + } + }) + .collect(); + rule.is_satisfied(field, new_state_merkle_root, proofs, &members).is_ok() + }) }) { Ok(()) diff --git a/src/process.rs b/src/process.rs index 033ace0..e1da12a 100644 --- a/src/process.rs +++ b/src/process.rs @@ -99,16 +99,37 @@ impl ProcessState { } /// This is a simplified and streamlined validation for obliteration state - fn handle_obliteration(&self) -> anyhow::Result<()> { + fn handle_obliteration(&self, members_list: &OutPointMemberMap) -> anyhow::Result<()> { // We need an Apophis role if let Some(apophis) = self.roles.get(SpecialRoles::APOPHIS.to_string().as_str()) { - apophis.is_satisfied(vec!["".to_owned()], [0u8; 32], &self.validation_tokens) + // Apophis should have only one rule + if apophis.validation_rules.len() != 1 { return Err(anyhow::Error::msg("Should have only one rule")); }; + let obliteration_rule = apophis.validation_rules.get(0).unwrap(); + + let empty_field = ""; + // This rule should have only one empty string as field + if obliteration_rule.fields.len() != 1 { return Err(anyhow::Error::msg("Should have only one field")); }; + if obliteration_rule.fields.get(0).unwrap() != empty_field { return Err(anyhow::Error::msg("Field should be empty")); }; + + let members: Vec<&Member> = apophis.members.iter() + .filter_map(|outpoint| { + members_list.0.get(outpoint) + }) + .collect(); + + if apophis.validation_rules.iter().all( + |r| r.is_satisfied(empty_field, [0u8; 32], &self.validation_tokens, &members).is_ok() + ) { + Ok(()) + } else { + Err(anyhow::Error::msg("Apophis is not satisfied")) + } } else { Err(anyhow::Error::msg("Missing an apophis role")) } } - pub fn is_valid(&self, previous_state: Option<&ProcessState>) -> anyhow::Result<()> { + pub fn is_valid(&self, previous_state: Option<&ProcessState>, members_list: &OutPointMemberMap) -> anyhow::Result<()> { if self.validation_tokens.is_empty() { return Err(anyhow::anyhow!( "Can't validate a state with no proofs attached" @@ -117,7 +138,7 @@ impl ProcessState { if self.validation_tokens.get(0).unwrap().get_message() == OBLITERATION_MSG { // We're dealing with a destruction update - return self.handle_obliteration(); + return self.handle_obliteration(members_list); } // Compute modified fields @@ -155,11 +176,16 @@ impl ProcessState { applicable_roles.into_iter().any(|role_def| { role_def.validation_rules.iter().any(|rule| { + let members: Vec<&Member> = role_def.members.iter() + .filter_map(|outpoint| { + members_list.0.get(outpoint) + }) + .collect(); rule.is_satisfied( field, self.state_id, &self.validation_tokens, - &role_def.members, + members.as_slice(), ).is_ok() }) }) @@ -176,7 +202,7 @@ impl ProcessState { self.state_id == [0u8; 32] } - pub fn get_fields_to_validate_for_member(&self, member: &Member) -> anyhow::Result> { + pub fn get_fields_to_validate_for_member(&self, member: &OutPoint) -> anyhow::Result> { let mut res: HashSet = HashSet::new(); // Are we in that role?