Add validation logic to ValidationRules and RuleDefinition
This commit is contained in:
parent
432dc4b8ea
commit
70bd927077
93
src/pcd.rs
93
src/pcd.rs
@ -9,7 +9,7 @@ use serde_json::{Map, Value};
|
|||||||
use sp_client::{bitcoin::{hashes::{sha256t_hash_newtype, Hash, HashEngine}, hex::{DisplayHex, FromHex}, XOnlyPublicKey}, silentpayments::utils::SilentPaymentAddress};
|
use sp_client::{bitcoin::{hashes::{sha256t_hash_newtype, Hash, HashEngine}, hex::{DisplayHex, FromHex}, XOnlyPublicKey}, silentpayments::utils::SilentPaymentAddress};
|
||||||
use tsify::Tsify;
|
use tsify::Tsify;
|
||||||
|
|
||||||
use crate::crypto::AAD;
|
use crate::{crypto::AAD, signature::{AnkValidationNoHash, AnkValidationYesHash, Proof}};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, Tsify)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, Tsify)]
|
||||||
#[tsify(into_wasm_abi, from_wasm_abi)]
|
#[tsify(into_wasm_abi, from_wasm_abi)]
|
||||||
@ -181,6 +181,67 @@ impl ValidationRule {
|
|||||||
|
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_satisfied(&self, field: &str, new_state_hash: AnkPcdHash, proofs: &[&Proof], members: &[Member]) -> bool {
|
||||||
|
// Check if this rule applies to the field
|
||||||
|
if !self.fields.contains(&field.to_string()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let required_members = (members.len() as f32 * self.quorum).ceil() as usize;
|
||||||
|
let validating_members = members.iter()
|
||||||
|
.filter(|member| {
|
||||||
|
let member_proofs: Vec<&Proof> = proofs.iter()
|
||||||
|
.filter(|p| member.key_is_part_of_member(&p.get_key()))
|
||||||
|
.cloned()
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
self.satisfy_min_sig_member(member, new_state_hash, &member_proofs).is_ok()
|
||||||
|
})
|
||||||
|
.count();
|
||||||
|
|
||||||
|
validating_members >= required_members
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn satisfy_min_sig_member(&self, member: &Member, new_state_hash: AnkPcdHash, proofs: &[&Proof]) -> Result<()> {
|
||||||
|
let required_sigs = (member.get_addresses().len() as f32 * self.min_sig_member).ceil() as usize;
|
||||||
|
if required_sigs > proofs.len() {
|
||||||
|
// We can't have more proofs than registered devices for one member
|
||||||
|
return Err(Error::msg("More proofs than devices for member"));
|
||||||
|
} else if proofs.len() < required_sigs {
|
||||||
|
// Even if all proof are valid yes, we don't reach the quota
|
||||||
|
return Err(Error::msg("Not enough provided proofs to reach quota"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut yes_votes: Vec<Proof> = Vec::new();
|
||||||
|
let mut no_votes: Vec<Proof> = Vec::new();
|
||||||
|
|
||||||
|
// Compute both yes and no commitment
|
||||||
|
let yes = AnkValidationYesHash::from_commitment(new_state_hash).to_byte_array();
|
||||||
|
let no = AnkValidationNoHash::from_commitment(new_state_hash).to_byte_array();
|
||||||
|
|
||||||
|
// Validate proofs here
|
||||||
|
for proof in proofs {
|
||||||
|
if !proof.verify().is_ok() {
|
||||||
|
return Err(Error::msg("Invalid proof"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let signed_message = proof.get_message();
|
||||||
|
|
||||||
|
if signed_message == yes {
|
||||||
|
yes_votes.push(**proof);
|
||||||
|
} else if signed_message == no {
|
||||||
|
no_votes.push(**proof);
|
||||||
|
} else {
|
||||||
|
return Err(Error::msg("We don't know what this proof signs for"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if yes_votes.len() >= required_sigs {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(Error::msg("Not enough yes votes"))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Tsify)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Tsify)]
|
||||||
@ -190,6 +251,36 @@ pub struct RoleDefinition {
|
|||||||
pub validation_rules: Vec<ValidationRule>,
|
pub validation_rules: Vec<ValidationRule>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl RoleDefinition {
|
||||||
|
pub fn is_satisfied(&self, new_state: &Value, previous_state: &Value, proofs: &[&Proof]) -> bool {
|
||||||
|
// compute the modified fields
|
||||||
|
let modified_fields: Vec<String> = new_state.as_object().unwrap()
|
||||||
|
.iter()
|
||||||
|
.filter_map(|(key, value)| {
|
||||||
|
let previous_value = previous_state.as_object().unwrap().get(key);
|
||||||
|
if previous_value.is_none() || value != previous_value.unwrap() {
|
||||||
|
Some(key.clone())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let new_state_hash = AnkPcdHash::from_value(new_state);
|
||||||
|
|
||||||
|
// check that for each field we can satisfy at least one rule
|
||||||
|
modified_fields.iter().all(|field| {
|
||||||
|
self.validation_rules.iter().any(|rule| rule.is_satisfied(field, new_state_hash, proofs, &self.members))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_applicable_rules(&self, field: &str) -> Vec<&ValidationRule> {
|
||||||
|
self.validation_rules.iter()
|
||||||
|
.filter(|rule| rule.fields.contains(&field.to_string()))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn compare_maps(map1: &Map<String, Value>, map2: &Map<String, Value>) -> bool {
|
pub fn compare_maps(map1: &Map<String, Value>, map2: &Map<String, Value>) -> bool {
|
||||||
// First, check if both maps have the same keys
|
// First, check if both maps have the same keys
|
||||||
if map1.keys().collect::<Vec<&String>>() != map2.keys().collect::<Vec<&String>>() {
|
if map1.keys().collect::<Vec<&String>>() != map2.keys().collect::<Vec<&String>>() {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user