[bug] actually handle demiurge case
This commit is contained in:
parent
45b2dc1e43
commit
dada38ba0e
121
src/process.rs
121
src/process.rs
@ -8,7 +8,7 @@ use sp_client::{bitcoin::{OutPoint, Transaction}, silentpayments::SilentPaymentA
|
|||||||
use tsify::Tsify;
|
use tsify::Tsify;
|
||||||
|
|
||||||
use crate::{
|
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>> {
|
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
|
/// This is a simplified and streamlined validation for obliteration state
|
||||||
fn handle_obliteration(&self, apophis: &RoleDefinition, members_list: &OutPointMemberMap) -> anyhow::Result<()> {
|
fn handle_obliteration(&self, apophis: &RoleDefinition, members_list: &OutPointMemberMap) -> anyhow::Result<()> {
|
||||||
// Apophis should have only one rule
|
// 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.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")); };
|
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()
|
apophis.is_satisfied(vec![empty_field.to_owned()], [0u8; 32], &self.validation_tokens, &members_list)
|
||||||
.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"))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_pairing(&self, pairing_role: RoleDefinition, previous_addresses: Vec<SilentPaymentAddress>) -> anyhow::Result<()> {
|
fn handle_pairing(&self, pairing_role: RoleDefinition, previous_addresses: Vec<SilentPaymentAddress>) -> anyhow::Result<()> {
|
||||||
@ -131,7 +148,7 @@ impl ProcessState {
|
|||||||
vec![&previous_member]
|
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<()> {
|
pub fn is_valid(&self, previous_state: Option<&ProcessState>, members_list: &OutPointMemberMap) -> anyhow::Result<()> {
|
||||||
@ -176,7 +193,18 @@ impl ProcessState {
|
|||||||
// That's optional though
|
// That's optional though
|
||||||
SpecialRoles::Demiurge => {
|
SpecialRoles::Demiurge => {
|
||||||
// If we're not in initial state just ignore it
|
// 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
|
// Otherwise we just continue normal validation
|
||||||
}
|
}
|
||||||
// We already handled other special roles
|
// 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 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 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 {
|
let role_def1 = RoleDefinition {
|
||||||
members: vec![OutPoint::from_str(ALICE_BOB_PAIRING).unwrap()],
|
members: vec![OutPoint::from_str(ALICE_BOB_PAIRING).unwrap()],
|
||||||
validation_rules: vec![validation_rule1],
|
validation_rules: vec![validation_rule1],
|
||||||
@ -708,6 +741,7 @@ mod tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let roles: BTreeMap<String, RoleDefinition> = BTreeMap::from([
|
let roles: BTreeMap<String, RoleDefinition> = BTreeMap::from([
|
||||||
|
("demiurge".to_owned(), role_demiurge),
|
||||||
("role1".to_owned(), role_def1),
|
("role1".to_owned(), role_def1),
|
||||||
("role2".to_owned(), role_def2),
|
("role2".to_owned(), role_def2),
|
||||||
("role_roles".to_owned(), role_def_roles),
|
("role_roles".to_owned(), role_def_roles),
|
||||||
@ -715,7 +749,7 @@ mod tests {
|
|||||||
("apophis".to_owned(), role_def_apophis)
|
("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",
|
"field1": "value1",
|
||||||
"field2": "value2",
|
"field2": "value2",
|
||||||
})).unwrap();
|
})).unwrap();
|
||||||
@ -727,7 +761,7 @@ mod tests {
|
|||||||
"public2": "public2",
|
"public2": "public2",
|
||||||
})).unwrap();
|
})).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 {
|
fn create_pairing_process_one() -> ProcessState {
|
||||||
@ -838,6 +872,55 @@ mod tests {
|
|||||||
assert!(result.is_ok());
|
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]
|
#[test]
|
||||||
fn test_valid_pairing() {
|
fn test_valid_pairing() {
|
||||||
let mut pairing_first_state = create_pairing_process_one();
|
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, alice_key));
|
||||||
state.validation_tokens.push(Proof::new(message_hash_yes, bob_key));
|
state.validation_tokens.push(Proof::new(message_hash_yes, bob_key));
|
||||||
state.validation_tokens.push(Proof::new(message_hash_no, carol_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!(result.is_err());
|
||||||
assert_eq!(result.unwrap_err().to_string(), "Not enough valid proofs");
|
assert_eq!(result.unwrap_err().to_string(), "Not enough valid proofs");
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user