[bug] actually handle demiurge case

This commit is contained in:
NicolasCantu 2025-04-05 18:34:55 +02:00 committed by Nicolas Cantu
parent 45b2dc1e43
commit dada38ba0e

View File

@ -8,7 +8,7 @@ use sp_client::{bitcoin::{OutPoint, Transaction}, silentpayments::SilentPaymentA
use tsify::Tsify;
use crate::{
pcd::{Member, Pcd, PcdCommitments, RoleDefinition, Roles}, serialization::{deserialize_hex, hex_array_btree, serialize_hex, OutPointMemberMap}, signature::{AnkHash, AnkValidationNoHash, AnkValidationYesHash, Proof}, MutexExt, SpecialRoles, APOPHIS, PAIREDADDRESSES, PAIRING
pcd::{Member, Pcd, PcdCommitments, RoleDefinition, Roles, ValidationRule}, serialization::{deserialize_hex, hex_array_btree, serialize_hex, OutPointMemberMap}, signature::{AnkHash, AnkValidationNoHash, AnkValidationYesHash, Proof}, MutexExt, SpecialRoles, APOPHIS, PAIREDADDRESSES, PAIRING
};
fn extract_paired_addresses(paired_addresses: &Value) -> anyhow::Result<Vec<SilentPaymentAddress>> {
@ -82,6 +82,35 @@ impl ProcessState {
}
}
fn handle_demiurge(&self, demiurge_role: &RoleDefinition) -> anyhow::Result<RoleDefinition> {
if demiurge_role.members.is_empty() {
return Err(anyhow::Error::msg("Invalid demiurge role: members can't be empty"));
}
// validation_rules is empty
if !demiurge_role.validation_rules.is_empty() {
return Err(anyhow::Error::msg("Invalid demiurge role: validation_rules must be empty"));
}
// if demiurge_role.storages.is_empty() {
// return Err(anyhow::Error::msg("Invalid demiurge role: storages can't be empty"));
// }
let all_keys: Vec<String> = self.pcd_commitment.keys().map(|k| k.clone()).collect();
// define the rule
let validation_rule = ValidationRule::new(
1.0,
all_keys.clone(),
1.0
)?;
let role = RoleDefinition {
members: demiurge_role.members.clone(),
storages: demiurge_role.storages.clone(),
validation_rules: vec![validation_rule]
};
Ok(role)
}
/// This is a simplified and streamlined validation for obliteration state
fn handle_obliteration(&self, apophis: &RoleDefinition, members_list: &OutPointMemberMap) -> anyhow::Result<()> {
// Apophis should have only one rule
@ -93,19 +122,7 @@ impl ProcessState {
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"))
}
apophis.is_satisfied(vec![empty_field.to_owned()], [0u8; 32], &self.validation_tokens, &members_list)
}
fn handle_pairing(&self, pairing_role: RoleDefinition, previous_addresses: Vec<SilentPaymentAddress>) -> anyhow::Result<()> {
@ -131,7 +148,7 @@ impl ProcessState {
vec![&previous_member]
};
paired_addresses_rule.iter().next().unwrap().is_satisfied(PAIREDADDRESSES, self.state_id, &self.validation_tokens, members.as_slice())
paired_addresses_rule.get(0).unwrap().is_satisfied(PAIREDADDRESSES, self.state_id, &self.validation_tokens, members.as_slice())
}
pub fn is_valid(&self, previous_state: Option<&ProcessState>, members_list: &OutPointMemberMap) -> anyhow::Result<()> {
@ -176,7 +193,18 @@ impl ProcessState {
// That's optional though
SpecialRoles::Demiurge => {
// If we're not in initial state just ignore it
if previous_state.is_some() { return None; }
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) => {
log::error!("{}", e.to_string());
return None;
}
}
}
// Otherwise we just continue normal validation
}
// We already handled other special roles
@ -678,6 +706,11 @@ mod tests {
let validation_rule4 = ValidationRule::new(1.0, vec!["public1".to_owned(), "public2".to_owned()], 0.5).unwrap();
let apophis_rule = ValidationRule::new(1.0, vec!["".to_owned()], 1.0).unwrap();
let role_demiurge = RoleDefinition {
members: vec![OutPoint::from_str(ALICE_BOB_PAIRING).unwrap()],
validation_rules: vec![],
storages: vec![]
};
let role_def1 = RoleDefinition {
members: vec![OutPoint::from_str(ALICE_BOB_PAIRING).unwrap()],
validation_rules: vec![validation_rule1],
@ -708,6 +741,7 @@ mod tests {
};
let roles: BTreeMap<String, RoleDefinition> = BTreeMap::from([
("demiurge".to_owned(), role_demiurge),
("role1".to_owned(), role_def1),
("role2".to_owned(), role_def2),
("role_roles".to_owned(), role_def_roles),
@ -715,7 +749,7 @@ mod tests {
("apophis".to_owned(), role_def_apophis)
]);
let clear_pcd: BTreeMap<String, Value> = serde_json::from_value(json!({
let private_data: BTreeMap<String, Value> = serde_json::from_value(json!({
"field1": "value1",
"field2": "value2",
})).unwrap();
@ -727,7 +761,7 @@ mod tests {
"public2": "public2",
})).unwrap();
ProcessState::new(outpoint, Pcd::new(clear_pcd), Pcd::new(public_data), Roles::new(roles)).unwrap()
ProcessState::new(outpoint, Pcd::new(private_data), Pcd::new(public_data), Roles::new(roles)).unwrap()
}
fn create_pairing_process_one() -> ProcessState {
@ -838,6 +872,55 @@ mod tests {
assert!(result.is_ok());
}
#[test]
/// AliceBob is the demiurge
fn test_valid_demiurge() {
let mut state = dummy_process_state();
// We sign with Alice and Carol keys
let alice_key: SecretKey = create_alice_wallet()
.get_spend_key()
.try_into()
.unwrap();
let bob_key: SecretKey = create_bob_wallet()
.get_spend_key()
.try_into()
.unwrap();
let message_hash = state.get_message_hash(true).unwrap();
state.validation_tokens.push(Proof::new(message_hash, alice_key));
state.validation_tokens.push(Proof::new(message_hash, bob_key));
let result = state.is_valid(None, &OutPointMemberMap(get_members_map()));
assert!(result.is_ok());
}
#[test]
/// Carol tries to bypass demiurge role
fn test_error_demiurge() {
let mut state = dummy_process_state();
// We sign with Alice and Carol keys
let carol_key: SecretKey = create_alice_wallet()
.get_spend_key()
.try_into()
.unwrap();
let message_hash = state.get_message_hash(true).unwrap();
state.validation_tokens.push(Proof::new(message_hash, carol_key));
let result = state.is_valid(None, &OutPointMemberMap(get_members_map()));
assert!(result.is_err());
}
#[test]
/// Carol tries to bypass demiurge role
fn test_error_demiurge_not_init_state() {
let mut state = dummy_process_state();
// We sign with Alice and Carol keys
let carol_key: SecretKey = create_alice_wallet()
.get_spend_key()
.try_into()
.unwrap();
let message_hash = state.get_message_hash(true).unwrap();
state.validation_tokens.push(Proof::new(message_hash, carol_key));
let result = state.is_valid(Some(&dummy_process_state()), &OutPointMemberMap(get_members_map()));
assert!(result.is_err());
}
#[test]
fn test_valid_pairing() {
let mut pairing_first_state = create_pairing_process_one();
@ -1096,7 +1179,7 @@ mod tests {
state.validation_tokens.push(Proof::new(message_hash_yes, alice_key));
state.validation_tokens.push(Proof::new(message_hash_yes, bob_key));
state.validation_tokens.push(Proof::new(message_hash_no, carol_key));
let result = state.is_valid(None, &OutPointMemberMap(get_members_map()));
let result = state.is_valid(Some(&dummy_process_state()), &OutPointMemberMap(get_members_map()));
assert!(result.is_err());
assert_eq!(result.unwrap_err().to_string(), "Not enough valid proofs");
}