Add ValidationRule tests + some fixes
This commit is contained in:
parent
9ff9176498
commit
3f633cf594
252
src/pcd.rs
252
src/pcd.rs
@ -184,7 +184,7 @@ impl ValidationRule {
|
|||||||
|
|
||||||
pub fn is_satisfied(&self, field: &str, new_state_hash: AnkPcdHash, proofs: &[&Proof], members: &[Member]) -> bool {
|
pub fn is_satisfied(&self, field: &str, new_state_hash: AnkPcdHash, proofs: &[&Proof], members: &[Member]) -> bool {
|
||||||
// Check if this rule applies to the field
|
// Check if this rule applies to the field
|
||||||
if !self.fields.contains(&field.to_string()) {
|
if !self.fields.contains(&field.to_string()) || members.is_empty() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -204,11 +204,19 @@ impl ValidationRule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn satisfy_min_sig_member(&self, member: &Member, new_state_hash: AnkPcdHash, proofs: &[&Proof]) -> Result<()> {
|
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 proofs.len() == 0 {
|
||||||
if required_sigs > proofs.len() {
|
return Err(Error::msg("Can't validate with 0 proof"));
|
||||||
|
}
|
||||||
|
let registered_devices = member.get_addresses().len();
|
||||||
|
if proofs.len() > registered_devices {
|
||||||
// We can't have more proofs than registered devices for one member
|
// We can't have more proofs than registered devices for one member
|
||||||
return Err(Error::msg("More proofs than devices for member"));
|
return Err(Error::msg("More proofs than requirefor member"));
|
||||||
} else if proofs.len() < required_sigs {
|
}
|
||||||
|
|
||||||
|
let required_sigs = (registered_devices as f32 * self.min_sig_member).ceil() as usize;
|
||||||
|
// println!("required_sigs {} and proofs.len() {}", required_sigs, proofs.len());
|
||||||
|
|
||||||
|
if proofs.len() < required_sigs {
|
||||||
// Even if all proof are valid yes, we don't reach the quota
|
// Even if all proof are valid yes, we don't reach the quota
|
||||||
return Err(Error::msg("Not enough provided proofs to reach quota"));
|
return Err(Error::msg("Not enough provided proofs to reach quota"));
|
||||||
}
|
}
|
||||||
@ -329,3 +337,237 @@ fn compare_arrays(array1: &Vec<Value>, array2: &Vec<Value>) -> bool {
|
|||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use serde_json::json;
|
||||||
|
use sp_client::{bitcoin::{secp256k1::SecretKey, Network}, spclient::{SpClient, SpWallet, SpendKey}};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
use crate::{
|
||||||
|
pcd::{Member, AnkPcdHash},
|
||||||
|
signature::{AnkHash, Proof},
|
||||||
|
};
|
||||||
|
|
||||||
|
fn create_alice_wallet() -> SpWallet {
|
||||||
|
SpWallet::new(
|
||||||
|
SpClient::new(
|
||||||
|
"default".to_owned(),
|
||||||
|
SecretKey::from_str("a67fb6bf5639efd0aeb19c1c584dd658bceda87660ef1088d4a29d2e77846973").unwrap(),
|
||||||
|
SpendKey::Secret(SecretKey::from_str("a1e4e7947accf33567e716c9f4d186f26398660e36cf6d2e711af64b3518e65c").unwrap()),
|
||||||
|
None,
|
||||||
|
Network::Signet
|
||||||
|
).unwrap(),
|
||||||
|
None,
|
||||||
|
vec![]
|
||||||
|
).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_bob_wallet() -> SpWallet {
|
||||||
|
SpWallet::new(
|
||||||
|
SpClient::new(
|
||||||
|
"default".to_owned(),
|
||||||
|
SecretKey::from_str("4d9f62b2340de3f0bafd671b78b19edcfded918c4106baefd34512f12f520e9b").unwrap(),
|
||||||
|
SpendKey::Secret(SecretKey::from_str("dafb99602721577997a6fe3da54f86fd113b1b58f0c9a04783d486f87083a32e").unwrap()),
|
||||||
|
None,
|
||||||
|
Network::Signet
|
||||||
|
).unwrap(),
|
||||||
|
None,
|
||||||
|
vec![]
|
||||||
|
).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_validation_rule_new() {
|
||||||
|
// Valid input
|
||||||
|
let fields = vec!["field1".to_string(), "field2".to_string()];
|
||||||
|
let validation_rule = ValidationRule::new(0.5, fields.clone(), 0.5);
|
||||||
|
assert!(validation_rule.is_ok());
|
||||||
|
let rule = validation_rule.unwrap();
|
||||||
|
assert_eq!(rule.quorum, 0.5);
|
||||||
|
assert_eq!(rule.fields, fields);
|
||||||
|
assert_eq!(rule.min_sig_member, 0.5);
|
||||||
|
|
||||||
|
// Invalid quorum (< 0.0)
|
||||||
|
let validation_rule = ValidationRule::new(-0.1, fields.clone(), 0.5);
|
||||||
|
assert!(validation_rule.is_err());
|
||||||
|
|
||||||
|
// Invalid quorum (> 1.0)
|
||||||
|
let validation_rule = ValidationRule::new(1.1, fields.clone(), 0.5);
|
||||||
|
assert!(validation_rule.is_err());
|
||||||
|
|
||||||
|
// Invalid min_sig_member (< 0.0)
|
||||||
|
let validation_rule = ValidationRule::new(0.5, fields.clone(), -0.1);
|
||||||
|
assert!(validation_rule.is_err());
|
||||||
|
|
||||||
|
// Invalid min_sig_member (> 1.0)
|
||||||
|
let validation_rule = ValidationRule::new(0.5, fields.clone(), 1.1);
|
||||||
|
assert!(validation_rule.is_err());
|
||||||
|
|
||||||
|
// Empty fields
|
||||||
|
let validation_rule = ValidationRule::new(0.5, vec![], 0.5);
|
||||||
|
assert!(validation_rule.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_is_satisfied() {
|
||||||
|
let alice_wallet = create_alice_wallet();
|
||||||
|
let bob_wallet = create_bob_wallet();
|
||||||
|
|
||||||
|
let fields = vec!["field1".to_string(), "field2".to_string()];
|
||||||
|
let validation_rule = ValidationRule::new(0.5, fields.clone(), 0.5).unwrap();
|
||||||
|
|
||||||
|
let pcd = json!({"field1": "value1", "field2": "value2"});
|
||||||
|
let new_state_hash = AnkPcdHash::from_value(&pcd);
|
||||||
|
|
||||||
|
let validation_hash1 = AnkValidationYesHash::from_commitment(new_state_hash);
|
||||||
|
let validation_hash2 = AnkValidationNoHash::from_commitment(new_state_hash);
|
||||||
|
|
||||||
|
let alice_spend_key: SecretKey = alice_wallet.get_client().get_spend_key().try_into().unwrap();
|
||||||
|
let bob_spend_key: SecretKey = bob_wallet.get_client().get_spend_key().try_into().unwrap();
|
||||||
|
|
||||||
|
let alice_proof = Proof::new(AnkHash::ValidationNo(validation_hash2), alice_spend_key);
|
||||||
|
let bob_proof = Proof::new(AnkHash::ValidationYes(validation_hash1), bob_spend_key);
|
||||||
|
|
||||||
|
let members = vec![
|
||||||
|
Member::new(vec![SilentPaymentAddress::try_from(alice_wallet.get_client().get_receiving_address()).unwrap()]).unwrap(),
|
||||||
|
Member::new(vec![SilentPaymentAddress::try_from(bob_wallet.get_client().get_receiving_address()).unwrap()]).unwrap(),
|
||||||
|
];
|
||||||
|
|
||||||
|
// We test that the rule is satisfied with only bob proof
|
||||||
|
let result = validation_rule.is_satisfied(fields[0].as_str(), new_state_hash, &vec![&bob_proof], &members);
|
||||||
|
assert!(result);
|
||||||
|
// Since Alice voted no, rule shouldn't be satisfied only with her proof
|
||||||
|
let result = validation_rule.is_satisfied(fields[0].as_str(), new_state_hash, &vec![&alice_proof], &members);
|
||||||
|
assert!(!result);
|
||||||
|
// Since the quorum is 0.5, Bob yes should be enough to satisfy even with Alice's no
|
||||||
|
let result = validation_rule.is_satisfied(fields[0].as_str(), new_state_hash, &vec![&alice_proof, &bob_proof], &members);
|
||||||
|
assert!(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_is_satisfied_error_cases() {
|
||||||
|
let alice_wallet = create_alice_wallet();
|
||||||
|
let bob_wallet = create_bob_wallet();
|
||||||
|
|
||||||
|
let fields = vec!["field1".to_string(), "field2".to_string()];
|
||||||
|
let validation_rule = ValidationRule::new(0.5, fields.clone(), 0.5).unwrap();
|
||||||
|
|
||||||
|
let pcd = json!({"field1": "value1", "field2": "value2"});
|
||||||
|
let new_state_hash = AnkPcdHash::from_value(&pcd);
|
||||||
|
|
||||||
|
let validation_hash1 = AnkValidationYesHash::from_commitment(new_state_hash);
|
||||||
|
let validation_hash2 = AnkValidationNoHash::from_commitment(new_state_hash);
|
||||||
|
|
||||||
|
let alice_spend_key: SecretKey = alice_wallet.get_client().get_spend_key().try_into().unwrap();
|
||||||
|
let bob_spend_key: SecretKey = bob_wallet.get_client().get_spend_key().try_into().unwrap();
|
||||||
|
|
||||||
|
let alice_proof = Proof::new(AnkHash::ValidationNo(validation_hash2), alice_spend_key);
|
||||||
|
let bob_proof = Proof::new(AnkHash::ValidationYes(validation_hash1), bob_spend_key);
|
||||||
|
|
||||||
|
let proofs = vec![
|
||||||
|
&alice_proof,
|
||||||
|
&bob_proof
|
||||||
|
];
|
||||||
|
|
||||||
|
let members = vec![
|
||||||
|
Member::new(vec![SilentPaymentAddress::try_from(alice_wallet.get_client().get_receiving_address()).unwrap()]).unwrap(),
|
||||||
|
Member::new(vec![SilentPaymentAddress::try_from(bob_wallet.get_client().get_receiving_address()).unwrap()]).unwrap(),
|
||||||
|
];
|
||||||
|
|
||||||
|
// Test with empty members list
|
||||||
|
let result = validation_rule.is_satisfied(fields[0].as_str(), new_state_hash, &proofs, &vec![]);
|
||||||
|
assert!(!result);
|
||||||
|
|
||||||
|
// Test with no matching field
|
||||||
|
let result = validation_rule.is_satisfied("nonexistent_field", new_state_hash, &proofs, &members);
|
||||||
|
assert!(!result);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_is_satisfied_error_with_alice_providing_proofs_for_bob() {
|
||||||
|
let alice_wallet = create_alice_wallet();
|
||||||
|
let bob_wallet = create_bob_wallet();
|
||||||
|
|
||||||
|
let fields = vec!["field1".to_string(), "field2".to_string()];
|
||||||
|
let validation_rule = ValidationRule::new(1.0, fields.clone(), 0.5).unwrap();
|
||||||
|
|
||||||
|
let pcd = json!({"field1": "value1", "field2": "value2"});
|
||||||
|
let new_state_hash = AnkPcdHash::from_value(&pcd);
|
||||||
|
|
||||||
|
let validation_hash = AnkValidationYesHash::from_commitment(new_state_hash);
|
||||||
|
|
||||||
|
let alice_spend_key: SecretKey = alice_wallet.get_client().get_spend_key().try_into().unwrap();
|
||||||
|
|
||||||
|
// Both proofs are signed by Alice
|
||||||
|
let alice_proof_1 = Proof::new(AnkHash::ValidationYes(validation_hash), alice_spend_key);
|
||||||
|
let alice_proof_2 = Proof::new(AnkHash::ValidationYes(validation_hash), alice_spend_key);
|
||||||
|
|
||||||
|
let proofs = vec![
|
||||||
|
&alice_proof_1,
|
||||||
|
&alice_proof_2
|
||||||
|
];
|
||||||
|
|
||||||
|
let members = vec![
|
||||||
|
Member::new(vec![SilentPaymentAddress::try_from(alice_wallet.get_client().get_receiving_address()).unwrap()]).unwrap(),
|
||||||
|
Member::new(vec![SilentPaymentAddress::try_from(bob_wallet.get_client().get_receiving_address()).unwrap()]).unwrap(),
|
||||||
|
];
|
||||||
|
|
||||||
|
// Test case where both proofs are signed by Alice, but both Alice and Bob are passed as members
|
||||||
|
let result = validation_rule.is_satisfied(fields[0].as_str(), new_state_hash, &proofs, &members);
|
||||||
|
assert!(!result);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_is_satisfied_error_quorum_half_with_alice_providing_two_proofs() {
|
||||||
|
let alice_wallet = create_alice_wallet();
|
||||||
|
let bob_wallet = create_bob_wallet();
|
||||||
|
|
||||||
|
let fields = vec!["field1".to_string(), "field2".to_string()];
|
||||||
|
let validation_rule = ValidationRule::new(0.5, fields.clone(), 0.5).unwrap();
|
||||||
|
|
||||||
|
let pcd = json!({"field1": "value1", "field2": "value2"});
|
||||||
|
let new_state_hash = AnkPcdHash::from_value(&pcd);
|
||||||
|
|
||||||
|
let validation_hash = AnkValidationYesHash::from_commitment(new_state_hash);
|
||||||
|
|
||||||
|
let alice_spend_key: SecretKey = alice_wallet.get_client().get_spend_key().try_into().unwrap();
|
||||||
|
|
||||||
|
// Both proofs are signed by Alice
|
||||||
|
let alice_proof_1 = Proof::new(AnkHash::ValidationYes(validation_hash), alice_spend_key);
|
||||||
|
let alice_proof_2 = Proof::new(AnkHash::ValidationYes(validation_hash), alice_spend_key);
|
||||||
|
|
||||||
|
let proofs = vec![
|
||||||
|
&alice_proof_1,
|
||||||
|
&alice_proof_2
|
||||||
|
];
|
||||||
|
|
||||||
|
let members = vec![
|
||||||
|
Member::new(vec![SilentPaymentAddress::try_from(alice_wallet.get_client().get_receiving_address()).unwrap()]).unwrap(),
|
||||||
|
Member::new(vec![SilentPaymentAddress::try_from(bob_wallet.get_client().get_receiving_address()).unwrap()]).unwrap(),
|
||||||
|
];
|
||||||
|
|
||||||
|
// Test case where quorum is 0.5, but Alice provides two proofs. This should fail since the quorum requires different members.
|
||||||
|
let result = validation_rule.is_satisfied(fields[0].as_str(), new_state_hash, &proofs, &members);
|
||||||
|
assert!(!result);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_satisfy_min_sig_member() {
|
||||||
|
let fields = vec!["field1".to_string()];
|
||||||
|
let validation_rule = ValidationRule::new(0.5, fields, 0.5).unwrap();
|
||||||
|
|
||||||
|
let alice_wallet = create_alice_wallet();
|
||||||
|
|
||||||
|
let member = Member::new(vec![SilentPaymentAddress::try_from(alice_wallet.get_client().get_receiving_address()).unwrap()]).unwrap();
|
||||||
|
let pcd = json!({"field1": "value1"});
|
||||||
|
let new_state_hash = AnkPcdHash::from_value(&pcd);
|
||||||
|
let alice_spend_key: SecretKey = alice_wallet.get_client().get_spend_key().try_into().unwrap();
|
||||||
|
|
||||||
|
let proof = Proof::new(AnkHash::ValidationYes(AnkValidationYesHash::from_commitment(new_state_hash)), alice_spend_key);
|
||||||
|
let proofs = vec![&proof];
|
||||||
|
|
||||||
|
let result = validation_rule.satisfy_min_sig_member(&member, new_state_hash, &proofs);
|
||||||
|
assert!(result.is_ok()); // Example check - make more meaningful assertions based on real Proof and Member implementations
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user